[
  {
    "path": ".github/FUNDING.yml",
    "content": "open_collective: go-kratos\ngithub: [go-kratos]\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug-report.md",
    "content": "---\nname: \"\\U0001F41B Bug Report\"\nabout: Report something that's broken. Please ensure your kratos version is still supported.\ntitle: ''\nlabels: bug\nassignees: ''\n---\n\n<!--\nPlease answer these questions before submitting your issue. Thanks!\nFor questions please use one of our forums: https://go-kratos.dev/docs/getting-started/faq\n-->\n\n#### What happened:\n\n#### What you expected to happen:\n\n#### How to reproduce it (as minimally and precisely as possible):\n\n#### Anything else we need to know?:\n\n#### Environment:\n- Kratos version (use `kratos -v`):\n- Go version (use `go version`):\n- OS (e.g: `cat /etc/os-release`):\n- Others:\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/config.yml",
    "content": "blank_issues_enabled: false\ncontact_links:\n  - name: Documentation issue\n    url: https://go-kratos.dev/docs/\n    about: For documentation issues, open a pull request at the go-kratos/go-kratos.dev repository\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature-request.md",
    "content": "---\nname: \"\\U0001F4A1 Feature Request\"\nabout: For ideas or feature requests, start a new discussion.\ntitle: \"[Feature]\"\nlabels: feature\nassignees: ''\n---\n\nPlease see the FAQ in our main README.md before submitting your issue.\n\n<!--\nIn order to accurately distinguish that the needs put forward by users are the needs of most users and reasonable needs, solicit community opinions through the process, and the features adopted by the community will be realized as new functions.\n\nIn order to make the proposal process as simple as possible, the process includes three stages: feature request - > proposal - > pull-request, where feature, proposal is issue and pull-request is the specific function implementation.\n\n### Feature-request\n\nIn order to help the community correctly understand the requirements of the feature, the feature request issue needs to describe the functional requirements and relevant references or documents in detail. And the feature request issue can contain the basic description of the function, which can be used as a reference for the function implementation in the proposal.\n\n### Proposal\n\nProposal contains the basic implementation methods of functions, such as interface definition, general usage of functions, etc.\n\n### Pull-request\n\nAfter the function is realized, a merge request will be initiated to associate the proposal issue with the function issue. After the merger is completed, all questions will be closed and the process will end.\n\n### Decision process\n\nWhen more than five maintainer members agree to implement the feature, a proposal issue will be created for detailed design. The status of the proposal is divided into: under discussion, finalized and abandoned. After reaching the final status, start specific implementation (PR can also be implemented synchronously during the discussion)\n\n### Final decision maker mechanism\n\nIf the maintainer team members have major differences on a requirement, the final decision is made by @Terry Mao.\n-->\n\n### What problem is the feature used to solve?\n<!--\nexample:\n    We hope to add event interface to Kratos framework to access middleware such as Kafka and rabbitmq\n-->\n\n### Requirements description of the feature\n<!--\nexample:\n    The event interface should be added to Kratos. The interface should contain subscribers and publishers, and the message body should contain key value head\n-->\n\n### References\n<!--\nexample:\n    - [nats](http://xxxxx)\n    - [kafka](http://xxxxx)\n    - [rabbitmq](http://xxxxx)\n-->\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/proposal.md",
    "content": "---\nname: \"\\U0001F9F1 Proposal Request\"\nabout: Implementation draft of feature.\ntitle: \"[Proposal]\"\nlabels: proposal\nassignees: ''\n---\n\nPlease see the FAQ in our main README.md before submitting your issue.\n\n<!--\nIn order to accurately distinguish that the needs put forward by users are the needs of most users and reasonable needs, solicit community opinions through the process, and the features adopted by the community will be realized as new functions.\n\nIn order to make the proposal process as simple as possible, the process includes three stages: feature request - > proposal - > pull-request, where feature, proposal is issue and pull-request is the specific function implementation.\n\n### Feature-request\n\nIn order to help the community correctly understand the requirements of the feature, the feature request issue needs to describe the functional requirements and relevant references or documents in detail. And the feature request issue can contain the basic description of the function, which can be used as a reference for the function implementation in the proposal.\n\n### Proposal\n\nProposal contains the basic implementation methods of functions, such as interface definition, general usage of functions, etc.\n\n### Pull-request\n\nAfter the function is realized, a merge request will be initiated to associate the proposal issue with the function issue. After the merger is completed, all questions will be closed and the process will end.\n\n### Decision process\n\nWhen more than five maintainer members agree to implement the feature, a proposal issue will be created for detailed design. The status of the proposal is divided into: under discussion, finalized and abandoned. After reaching the final status, start specific implementation (PR can also be implemented synchronously during the discussion)\n\n### Final decision maker mechanism\n\nIf the maintainer team members have major differences on a requirement, the final decision is made by @Terry Mao.\n-->\n\n### Proposal description\n<!--\nexample:\nAdd event interface for accessing message oriented middleware\n-->\n\n### Implementation mode\n<!--\n```go\nexample:\ntype Message interface {\n    Key() string\n    Value() []byte\n    Header() map[string]string\n    Ack() error\n    Nack() error\n}\n\ntype Handler func(context.Context, Message) error\n\ntype Event interface {\n    Send(ctx context.Context, key string, value []byte]) error\n    Receive(ctx context.Context, handler Handler) error\n    Close() error\n}\n````\n-->\n\n### Usage demonstration\n<!--\nexample:\n```go\nmsg := kafka.NewMessage(\"kratos\", []byte(\"hello world\"), map[string]string{\n\t\t\"user\":  \"kratos\",\n\t\t\"phone\": \"123456\",\n\t})\nerr := sender.Send(context.Background(), msg)\n```\n-->\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/question.md",
    "content": "---\nname: \"\\U0001F680 Question\"\nabout: Ask a question about Kratos.\ntitle: \"[Question]\"\nlabels: question\nassignees: ''\n\n---\n\nPlease see the FAQ in our main README.md before submitting your issue.\n"
  },
  {
    "path": ".github/dependabot.yml",
    "content": "---\nversion: 2\nupdates:\n  - package-ecosystem: \"github-actions\"\n    directory: \"/\"\n    schedule:\n      interval: \"monthly\"\n"
  },
  {
    "path": ".github/pull_request_template.md",
    "content": "<!--\n🎉 Thanks for sending a pull request to Kratos! Here are some tips for you:\n\n1. If this is your first time contributing to Kratos, please read our contribution guide: https://go-kratos.dev/docs/community/contribution/\n2. Ensure you have added or ran the appropriate tests and lint for your PR, please use `make lint` and `make test` before filing your PR, use `make clean` to tidy your go mod.\n3. If the PR is unfinished, you may need to mark it as a WIP(Work In Progress) PR or Draft PR\n4. Please use a semantic commits format title, such as `<type>[optional scope]: <description>`, see: https://go-kratos.dev/docs/community/contribution#type\n5. at the same time, please note that similar work should be submitted in one PR as far as possible to reduce the workload of reviewers. Do not split a work into multiple PR unless it should.\n-->\n\n<!--\n🎉 感谢您向 Kratos 发送 PR！以下是一些提示：\n如果这是你第一次为 Kratos 贡献，请阅读我们的贡献指南：https://go-kratos.dev/zh-cn/docs/community/contribution/\n2、确保您已经为您的 PR 添加或运行了适当的测试和lint，请在提交PR之前使用“make lint”和“make test”，使用“make clean”整理您的 go.mod。\n3、如果 PR 未完成，您可能需要将其标记为 WIP（Work In Progress）PR 或 Draft PR\n4、请使用语义提交格式标题，如“<类型>[可选范围]：<说明>`，请参阅：https://go-kratos.dev/zh-cn/docs/community/contribution/#type\n5. 同时请注意，同类的工作请尽量在一个PR中提交，以减轻 review 者的工作负担，不要把一项工作拆分成很多个PR，除非它应该这样做。\n-->\n\n#### Description (what this PR does / why we need it):\n<!--\n* The description should include the motivation for this PR or contrast this with previous behavior\n-->\n\n#### Which issue(s) this PR fixes (resolves / be part of):\n<!--\n* Automatically closes linked issue when PR is merged.\n* If your PR is not fully resolved the issue, please use `part of #<issue number>` instead.\n\nUsage: `fixes/resolves #<issue number>`, or `fixes/resolves (paste link of issue)`.\n-->\n\n#### Other special notes for the reviewers:\n<!--\n* Some things that need extra attention for the reviewers\n* Some additional notes, TODO list, etc.\n-->\n"
  },
  {
    "path": ".github/semantic.yml",
    "content": "titleOnly: true\ncommitOnly: false\ntitleAndCommits: false\nscopes:\n  - api\n  - cmd\n  - config\n  - contrib\n  - docs\n  - encoding\n  - hack\n  - internal\n  - log\n  - metadata\n  - metrics\n  - middleware\n  - registry\n  - selector\n  - third_party\n  - transport\ntypes:\n  - deps\n  - feat\n  - fix\n  - docs\n  - style\n  - refactor\n  - perf\n  - test\n  - build\n  - ci\n  - chore\n  - revert\n"
  },
  {
    "path": ".github/stable.yml",
    "content": "daysUntilStale: 30\ndaysUntilClose: 3\nexemptLabels:\n  - pinned\n  - security\n  - bug\nstaleLabel: wontfix\nmarkComment: >\n  This issue has been automatically marked as stale because it has not had\n  recent activity. It will be closed if no further activity occurs. Thank you\n  for your contributions.\ncloseComment: true\n"
  },
  {
    "path": ".github/workflows/codeql-analysis.yml",
    "content": "name: \"CodeQL\"\n\non:\n  push:\n    branches: [ main, v1.0.x ]\n\njobs:\n  analyze:\n    name: Analyze\n    runs-on: ubuntu-latest\n\n    steps:\n    - name: Checkout repo\n      uses: actions/checkout@v6\n\n    - name: Initialize CodeQL\n      uses: github/codeql-action/init@v4\n      with:\n        languages: go\n\n    - name: CodeQL Analysis\n      uses: github/codeql-action/analyze@v4\n"
  },
  {
    "path": ".github/workflows/comment-check.yml",
    "content": "name: Non-English Comments Check\n\non:\n  pull_request_target:\n    types: [opened, synchronize, reopened]\n    branches:\n      - main\n  # workflow_dispatch:\n\njobs:\n  non-english-comments-check:\n    runs-on: ubuntu-latest\n    permissions:\n      contents: read\n      pull-requests: write\n\n    env:\n      # Directories to be excluded\n      EXCLUDE_DIRS: \".git docs tests scripts assets node_modules build\"\n      # Files to be excluded\n      EXCLUDE_FILES: \".md .txt .html .css .min.js .mdx\"\n\n    steps:\n      - uses: actions/checkout@v6\n        with:\n          ref: ${{ github.event.pull_request.head.ref }}\n          repository: ${{ github.event.pull_request.head.repo.full_name }}\n          fetch-depth: 0\n\n      # - name: Search for Non-English comments in the entire repository\n      #   run: |\n      #     set -e\n      #     # Define the regex pattern to match Chinese characters\n      #     pattern='[\\p{Han}]'\n      \n      #     # Use find to get all files in the repository\n      #     all_files=$(find . -type f)\n      \n      #     # Loop over each file in the repository\n      #     for file in $all_files; do\n      #       # Skip files in excluded directories\n      #       skip_file=false\n      #       for dir in ${EXCLUDE_DIRS}; do\n      #         if [[ \"$file\" == ./$dir/* ]]; then\n      #           skip_file=true\n      #           break\n      #         fi\n      #       done\n      \n      #       # Skip files matching excluded patterns\n      #       for file_pattern in ${EXCLUDE_FILES}; do\n      #         if [[ \"$file\" == *$file_pattern ]]; then\n      #           skip_file=true\n      #           break\n      #         fi\n      #       done\n      \n      #       # If the file matches any exclude pattern, skip it\n      #       if [ \"$skip_file\" = true ]; then\n      #         continue\n      #       fi\n      \n      #       # Use grep to find all comments containing Non-English characters in filtered files\n      #       grep_output=$(grep -PnH \"$pattern\" \"$file\" || true)\n      #       if [ -n \"$grep_output\" ]; then\n      #         # Insert a tab after the line number, keeping the colon between the file path and line number\n      #         formatted_output=$(echo \"$grep_output\" | sed 's/^\\(.*:[0-9]\\+\\):/\\1\\t/')\n      #         echo \"$formatted_output\" >> non_english_comments.txt  # Save to file\n      #       fi\n      #     done\n\n      - name: Search for Non-English comments in PR diff files\n        run: |\n          set -e\n          # Define the regex pattern to match Chinese characters\n          pattern='[\\p{Han}]'\n            \n          # Get the list of files changed in this PR compared to the base branch\n          changed_files=$(git diff --name-only ${{ github.event.pull_request.base.sha }} ${{ github.event.pull_request.head.sha }})\n            \n          # Loop over each changed file\n          for file in $changed_files; do\n            # Skip files in excluded directories\n            skip_file=false\n            for dir in ${EXCLUDE_DIRS}; do\n              if [[ \"$file\" == ./$dir/* ]]; then\n                skip_file=true\n                break\n              fi\n            done\n       \n            # Skip files matching excluded patterns\n            for file_pattern in ${EXCLUDE_FILES}; do\n              if [[ \"$file\" == *$file_pattern ]]; then\n                skip_file=true\n                break\n              fi\n            done\n       \n            # If the file matches any exclude pattern, skip it\n            if [ \"$skip_file\" = true ]; then\n              continue\n            fi\n       \n            # Use grep to find all comments containing Non-English characters in filtered files\n            grep_output=$(grep -PnH \"$pattern\" \"$file\" || true)\n            if [ -n \"$grep_output\" ]; then\n              # Insert a tab after the line number, keeping the colon between the file path and line number\n              formatted_output=$(echo \"$grep_output\" | sed 's/^\\(.*:[0-9]\\+\\):/\\1\\t/')\n              echo \"$formatted_output\" >> non_english_comments.txt  # Save to file\n            fi\n          done\n\n      - name: Store non-English comments in ENV\n        run: |\n          # Store the entire content of non_english_comments.txt into an environment variable\n          if [ -f non_english_comments.txt ]; then\n            NON_ENGLISH_COMMENTS=$(cat non_english_comments.txt)\n            echo \"NON_ENGLISH_COMMENTS<<EOF\" >> $GITHUB_ENV\n            echo \"$NON_ENGLISH_COMMENTS\" >> $GITHUB_ENV\n            echo \"EOF\" >> $GITHUB_ENV\n          fi\n\n      - name: Output non-English comments if found\n        run: |\n          if [ -s non_english_comments.txt ]; then\n            echo \"Non-English comments found in the following locations:\"\n            cat non_english_comments.txt\n            exit 1  # terminate the workflow\n          else\n            echo \"No Non-English comments found.\"\n          fi\n      \n      - name: Find Comment\n        if: failure() && github.event_name != 'workflow_dispatch'\n        uses: peter-evans/find-comment@v4.0.0\n        id: fc\n        with:\n          issue-number: ${{ github.event.pull_request.number }}\n          comment-author: \"kratos-ci-bot\"\n          body-includes: Non-English comments were found in the following locations\n\n      - name: Comment on PR if errors found\n        if: failure() && github.event_name != 'workflow_dispatch' # This step runs only if the previous step fails\n        uses: peter-evans/create-or-update-comment@v5.0.0\n        with:\n          token: ${{ secrets.BOT_GITHUB_TOKEN }}\n          issue-number: ${{ github.event.pull_request.number }}\n          comment-id: ${{ steps.fc.outputs.comment-id }}\n          edit-mode: replace\n          body: |\n            ⚠️ Non-English comments were found in the following locations:\n            ```\n            ${{ env.NON_ENGLISH_COMMENTS }}\n            ```\n"
  },
  {
    "path": ".github/workflows/gitee-sync.yml",
    "content": "on:\n  push:\n    branches:\n      - main\n    tags:\n      - \"*\"\n\nname: Sync to Gitee\njobs:\n  run:\n    name: Run\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout source code\n        uses: actions/checkout@v6\n      - name: Mirror Github to Gitee\n        uses: Yikun/hub-mirror-action@v1.5\n        with:\n          src: github/go-kratos\n          dst: gitee/go-kratos\n          dst_key: ${{ secrets.GITEE_PRIVATE_KEY }}\n          dst_token: ${{ secrets.GITEE_TOKEN }}\n          account_type: org\n          timeout: 600\n          debug: true\n          force_update: true\n          static_list: \"kratos\"\n"
  },
  {
    "path": ".github/workflows/go.yml",
    "content": "name: Go\n\non:\n  push:\n    branches:\n      - main\n  pull_request:\n    branches:\n      - main\n  workflow_dispatch:\n\njobs:\n  build:\n    strategy:\n      matrix:\n        go: [1.22.x, 1.23.x, 1.24.x, 1.25.x]\n    name: build & test\n    runs-on: ubuntu-latest\n    services:\n      etcd:\n        image: gcr.io/etcd-development/etcd:v3.5.0\n        ports:\n          - 2379:2379\n        env:\n          ETCD_LISTEN_CLIENT_URLS: http://0.0.0.0:2379\n          ETCD_ADVERTISE_CLIENT_URLS: http://0.0.0.0:2379\n      consul:\n        image: hashicorp/consul:1.20\n        ports:\n          - 8500:8500\n      nacos:\n        image: nacos/nacos-server:v2.1.0\n        env:\n          MODE: standalone\n        ports:\n          - \"8848:8848\"\n          - \"9848:9848\"\n      polaris:\n        image: polarismesh/polaris-standalone:latest\n        ports:\n          - 8090:8090\n          - 8091:8091\n          - 8093:8093\n    steps:\n      - uses: actions/checkout@v6\n      - name: Set up Go\n        uses: actions/setup-go@v6\n        with:\n          go-version: ${{ matrix.go }}\n\n      - name: Setup Environment\n        run: |\n          echo \"GOPATH=$(go env GOPATH)\" >> $GITHUB_ENV\n          echo \"$(go env GOPATH)/bin\" >> $GITHUB_PATH\n\n      - name: Module cache\n        uses: actions/cache@v5\n        with:\n          path: |\n            ~/.cache/go-build\n            ~/go/pkg/mod\n          key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}\n          restore-keys: |\n            ${{ runner.os }}-go\n\n      - name: Build\n        run: go build ./...\n        env:\n          GOTOOLCHAIN: auto\n\n      - name: Test\n        run: make test-coverage\n        env:\n          GOTOOLCHAIN: auto\n\n      - name: Upload coverage to Codecov\n        run: bash <(curl -s https://codecov.io/bash)\n\n      - name: Kratos\n        run: |\n          cd cmd/kratos\n          go build ./...\n        env:\n          GOTOOLCHAIN: auto\n\n      - name: HTTP\n        run: |\n          cd cmd/protoc-gen-go-http\n          go build ./...\n          go test ./...\n        env:\n          GOTOOLCHAIN: auto\n"
  },
  {
    "path": ".github/workflows/issue-translator.yml",
    "content": "name: 'issue-translator'\non:\n  issue_comment:\n    types: [created]\n  issues:\n    types: [opened]\n\njobs:\n  build:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: usthe/issues-translate-action@v2.7\n        with:\n          IS_MODIFY_TITLE: true\n          CUSTOM_BOT_NOTE: Bot detected the issue body's language is not English, translate it automatically. 👯👭🏻🧑‍🤝‍🧑👫🧑🏿‍🤝‍🧑🏻👩🏾‍🤝‍👨🏿👬🏿\n          BOT_GITHUB_TOKEN: ${{ secrets.BOT_GITHUB_TOKEN }}\n"
  },
  {
    "path": ".github/workflows/lint.yml",
    "content": "name: Lint\n\non:\n  push:\n  pull_request:\n    branches:\n      - main\n  workflow_dispatch:\n\njobs:\n  resolve-modules:\n    name: resolve module\n    runs-on: ubuntu-latest\n    outputs:\n      matrix: ${{ steps.set-matrix.outputs.matrix }}\n    steps:\n      - name: Checkout Repo\n        uses: actions/checkout@v6\n\n      - id: set-matrix\n        run: ./hack/resolve-modules.sh\n\n  lint:\n    name: lint module\n    runs-on: ubuntu-latest\n    needs: resolve-modules\n    strategy:\n      matrix: ${{ fromJson(needs.resolve-modules.outputs.matrix) }}\n    steps:\n      - uses: actions/checkout@v6\n      - name: Lint\n        uses: golangci/golangci-lint-action@v7\n        with:\n          version: v2.0\n          working-directory: ${{ matrix.workdir }}\n          skip-pkg-cache: true\n"
  },
  {
    "path": ".gitignore",
    "content": "# Reference https://github.com/github/gitignore/blob/master/Go.gitignore\n# Binaries for programs and plugins\n*.exe\n*.exe~\n*.dll\n*.dylib\n\n# Test binary, built with `go test -c`\n*.test\n\n# Output of the go coverage tool, specifically when used with LiteIDE\n*.out\n\n# Dependency directories (remove the comment below to include it)\nvendor/\n\n# Go workspace file\ngo.work\ngo.work.sum\n\n# Compiled Object files, Static and Dynamic libs (Shared Objects)\n*.o\n*.a\n*.so\n\n# OS General\nThumbs.db\n.DS_Store\n.cache\n\n# project\n*.cert\n*.key\n*.log\nbin/\n\n# Develop tools\n.vscode/\n.idea/\n*.swp\n"
  },
  {
    "path": ".golangci.yml",
    "content": "version: \"2\"\nrun:\n  modules-download-mode: readonly\nlinters:\n  default: none\n  enable:\n    - bodyclose\n    - dogsled\n    - durationcheck\n    - errcheck\n    - goconst\n    - gocyclo\n    - govet\n    - ineffassign\n    - lll\n    - misspell\n    - mnd\n    - prealloc\n    - revive\n    - staticcheck\n    - unconvert\n    - unused\n    - wastedassign\n    - whitespace\n  settings:\n    gocyclo:\n      min-complexity: 50\n    govet:\n      enable:\n        - shadow\n    lll:\n      line-length: 160\n    misspell:\n      locale: US\n    mnd:\n      checks:\n        - case\n        - condition\n        - return\n    whitespace:\n      multi-func: true\n  exclusions:\n    generated: lax\n    presets:\n      - comments\n      - common-false-positives\n      - legacy\n      - std-error-handling\n    rules:\n      - linters:\n          - goconst\n        path: (.+)_test\\.go\n    paths:\n      - third_party$\n      - builtin$\n      - examples$\nformatters:\n  enable:\n    - gofmt\n    - gofumpt\n    - goimports\n  settings:\n    goimports:\n      local-prefixes:\n        - github.com/go-kratos\n  exclusions:\n    generated: lax\n    paths:\n      - third_party$\n      - builtin$\n      - examples$\n"
  },
  {
    "path": "CODE_OF_CONDUCT.md",
    "content": "# Contributor Covenant Code of Conduct\n\n## Our Pledge\n\nWe as members, contributors, and leaders pledge to make participation in our\ncommunity a harassment-free experience for everyone, regardless of age, body\nsize, visible or invisible disability, ethnicity, sex characteristics, gender\nidentity and expression, level of experience, education, socio-economic status,\nnationality, personal appearance, race, religion, or sexual identity\nand orientation.\n\nWe pledge to act and interact in ways that contribute to an open, welcoming,\ndiverse, inclusive, and healthy community.\n\n## Our Standards\n\nExamples of behavior that contributes to a positive environment for our\ncommunity include:\n\n* Demonstrating empathy and kindness toward other people\n* Being respectful of differing opinions, viewpoints, and experiences\n* Giving and gracefully accepting constructive feedback\n* Accepting responsibility and apologizing to those affected by our mistakes,\n  and learning from the experience\n* Focusing on what is best not just for us as individuals, but for the\n  overall community\n\nExamples of unacceptable behavior include:\n\n* The use of sexualized language or imagery, and sexual attention or\n  advances of any kind\n* Trolling, insulting or derogatory comments, and personal or political attacks\n* Public or private harassment\n* Publishing others' private information, such as a physical or email\n  address, without their explicit permission\n* Other conduct which could reasonably be considered inappropriate in a\n  professional setting\n\n## Enforcement Responsibilities\n\nCommunity leaders are responsible for clarifying and enforcing our standards of\nacceptable behavior and will take appropriate and fair corrective action in\nresponse to any behavior that they deem inappropriate, threatening, offensive,\nor harmful.\n\nCommunity leaders have the right and responsibility to remove, edit, or reject\ncomments, commits, code, wiki edits, issues, and other contributions that are\nnot aligned to this Code of Conduct, and will communicate reasons for moderation\ndecisions when appropriate.\n\n## Scope\n\nThis Code of Conduct applies within all community spaces, and also applies when\nan individual is officially representing the community in public spaces.\nExamples of representing our community include using an official e-mail address,\nposting via an official social media account, or acting as an appointed\nrepresentative at an online or offline event.\n\n## Enforcement\n\nInstances of abusive, harassing, or otherwise unacceptable behavior may be\nreported to the community leaders responsible for enforcement at\niammao@vip.qq.com.\nAll complaints will be reviewed and investigated promptly and fairly.\n\nAll community leaders are obligated to respect the privacy and security of the\nreporter of any incident.\n\n## Enforcement Guidelines\n\nCommunity leaders will follow these Community Impact Guidelines in determining\nthe consequences for any action they deem in violation of this Code of Conduct:\n\n### 1. Correction\n\n**Community Impact**: Use of inappropriate language or other behavior deemed\nunprofessional or unwelcome in the community.\n\n**Consequence**: A private, written warning from community leaders, providing\nclarity around the nature of the violation and an explanation of why the\nbehavior was inappropriate. A public apology may be requested.\n\n### 2. Warning\n\n**Community Impact**: A violation through a single incident or series\nof actions.\n\n**Consequence**: A warning with consequences for continued behavior. No\ninteraction with the people involved, including unsolicited interaction with\nthose enforcing the Code of Conduct, for a specified period of time. This\nincludes avoiding interactions in community spaces as well as external channels\nlike social media. Violating these terms may lead to a temporary or\npermanent ban.\n\n### 3. Temporary Ban\n\n**Community Impact**: A serious violation of community standards, including\nsustained inappropriate behavior.\n\n**Consequence**: A temporary ban from any sort of interaction or public\ncommunication with the community for a specified period of time. No public or\nprivate interaction with the people involved, including unsolicited interaction\nwith those enforcing the Code of Conduct, is allowed during this period.\nViolating these terms may lead to a permanent ban.\n\n### 4. Permanent Ban\n\n**Community Impact**: Demonstrating a pattern of violation of community\nstandards, including sustained inappropriate behavior, harassment of an\nindividual, or aggression toward or disparagement of classes of individuals.\n\n**Consequence**: A permanent ban from any sort of public interaction within\nthe community.\n\n## Attribution\n\nThis Code of Conduct is adapted from the [Contributor Covenant][homepage],\nversion 2.0, available at\nhttps://www.contributor-covenant.org/version/2/0/code_of_conduct.html.\n\nCommunity Impact Guidelines were inspired by [Mozilla's code of conduct\nenforcement ladder](https://github.com/mozilla/diversity).\n\n[homepage]: https://www.contributor-covenant.org\n\nFor answers to common questions about this code of conduct, see the FAQ at\nhttps://www.contributor-covenant.org/faq. Translations are available at\nhttps://www.contributor-covenant.org/translations.\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "The kratos community wants to be helped by a wide range of developers, so you'd like to take a few minutes to read this guide before you mention the problem or pull request.\n\n## Reporting Bug or Fixing Bugs\nWe use GitHub issues to manage issues. If you want to submit , first make sure you've searched for existing issues, pull requests and read our [FAQ](https://go-kratos.dev/docs/intro/faq).\n\nWhen submitting a bug report, use the issue template we provide to clearly describe the problems encountered and how to reproduce, and if convenient it is best to provide a minimal reproduce repository.\n\n## Adding new features\n\nIn order to accurately distinguish whether the needs put forward by users are the needs or reasonable needs of most users, solicit opinions from the community through the proposal process, and the proposals adopted by the community will be realized as new feature.\nIn order to make the proposal process as simple as possible, the process includes three stages: proposal, feature and PR, in which proposal, feature is issue and PR is the specific function implementation.\nIn order to facilitate the community to correctly understand the requirements of the proposal, the proposal issue needs to describe the functional requirements and relevant references or literature in detail.\nWhen most community users agree with this proposal, they will create a feature issue associated with the proposal issue.\nThe feature issue needs to describe the implementation method and function demonstration in detail as a reference for the final function implementation.\nAfter the function is implemented, a merge request will be initiated to associate the proposal issue and feature issue.\nAfter the merge is completed, Close all issues.\n\n## How to submit code\nIf you've never submitted code on GitHub, follow these steps:\n\n- First, please fork items to your GitHub account\n- Then create a new feature branch based on the main branch and name it features such as feature-log\n- Write code\n- Submit code to the far end branch\n- Submit a PR request in github\n- Wait for review and merge to the main branch\n\n**Note That when you submit a PR request, you first ensure that the code uses the correct coding specifications and that there are complete test cases, and that the information in the submission of the PR is best associated with the relevant issue to ease the workload of the auditor.**\n\n## Conventional Commits\n\n```\n<type>[optional scope]: <description>\n\n[optional body]\n\n[optional footer(s)]\n```\n\n> More: [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/#summary)\n\n### type\n\nThere are the following types of commit:\n\n#### Main\n\n- **fix**: A bug fix\n- **feat**: A new feature\n- **deps**: Changes external dependencies\n- **break**: Changes has break change\n\n#### Other\n\n- **docs**: Documentation only changes\n- **refactor**: A code change that neither fixes a bug nor adds a feature\n- **style**: Changes that do not affect the meaning of the code (white-space, formatting, etc)\n- **test**: Adding missing tests or correcting existing tests\n- **chore** Daily work, examples, etc.\n- **ci**: Changes to our CI configuration files and scripts\n\n### scope\n\nThe following is the list of supported scopes:\n\n- transport\n- examples\n- middleware\n- config\n- cmd\n- etc.\n\n### description\n\nThe description contains a succinct description of the change\n\n- use the imperative, present tense: \"change\" not \"changed\" nor \"changes\"\n- don't capitalize the first letter\n- no dot (.) at the end\n\n### body\n\nThe body should include the motivation for the change and contrast this with previous behavior.\n\n### footer\n\nThe footer should contain any information about **Breaking Changes** and is also the place to reference GitHub issues that this commit Closes.\n\n### examples\n\n#### Only commit message\n```\nfix: The log debug level should be -1\n```\n\n#### Attention\n```\nrefactor!(transport/http): replacement underlying implementation\n```\n\n#### Full commit message\n```\nfix(log): [BREAKING-CHANGE] unable to meet the requirement of log Library\n\nExplain the reason, purpose, realization method, etc.\n\nClose #777\nDoc change on doc/#111\nBREAKING CHANGE:\n  Breaks log.info api, log.log should be used instead\n```\n## Release\n\nYou can use `kratos changelog dev` to generate a change log during.\n\nThe following is the list of supported types:\n\n- Breaking Change\n- Dependencies\n- Bug Fixes\n- Others\n"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2020 go-kratos\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.\n"
  },
  {
    "path": "Makefile",
    "content": "user\t:=\t$(shell whoami)\nrev\t\t:= \t$(shell git rev-parse --short HEAD)\nos\t\t:=\t$(shell uname)\n\n# GOBIN > GOPATH > INSTALLDIR\n# Mac OS X\nifeq ($(os),Darwin)\nGOBIN\t:=\t$(shell echo $(GOBIN) | cut -d':' -f1)\nGOPATH\t:=\t$(shell echo $(GOPATH) | cut -d':' -f1)\nendif\n\n# Linux\nifeq ($(os),Linux)\nGOBIN\t:=\t$(shell echo $(GOBIN) | cut -d':' -f1)\nGOPATH\t:=\t$(shell echo $(GOPATH) | cut -d':' -f1)\nendif\n\n# Windows\nifneq ($(findstring MINGW,$(shell uname -s)),)\nGOBIN := $(shell echo \"$(GOBIN)\" | sed 's|\\\\|/|g' | cut -d';' -f1 | sed 's|^\\([A-Za-z]\\):|/\\1|')\nGOPATH := $(shell echo \"$(GOPATH)\" | sed 's|\\\\|/|g' | cut -d';' -f1 | sed 's|^\\([A-Za-z]\\):|/\\1|')\nendif\nBIN\t\t:= \"\"\n\nTOOLS_SHELL=\"./hack/tools.sh\"\n# golangci-lint\nLINTER := bin/golangci-lint\n\n# check GOBIN\nifneq ($(GOBIN),)\n\tBIN=$(GOBIN)\nelse\n# check GOPATH\n\tifneq ($(GOPATH),)\n\t\tBIN=$(GOPATH)/bin\n\tendif\nendif\n\n$(LINTER):\n\tcurl -SL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s latest\n\nall:\n\t@cd cmd/kratos && go build && cd - &> /dev/null\n\t@cd cmd/protoc-gen-go-errors && go build && cd - &> /dev/null\n\t@cd cmd/protoc-gen-go-http && go build && cd - &> /dev/null\n\n.PHONY: install\ninstall: all\nifeq ($(user),root)\n#root, install for all user\n\t@cp ./cmd/kratos/kratos /usr/bin\n\t@cp ./cmd/protoc-gen-go-errors/protoc-gen-go-errors /usr/bin\n\t@cp ./cmd/protoc-gen-go-http/protoc-gen-go-http /usr/bin\nelse\n#!root, install for current user\n\t$(shell if [ -z '$(BIN)' ]; then read -p \"Please select installdir: \" REPLY; mkdir -p $${REPLY};\\\n\tcp ./cmd/kratos/kratos $${REPLY}/;cp ./cmd/protoc-gen-go-errors/protoc-gen-go-errors $${REPLY}/;cp ./cmd/protoc-gen-go-http/protoc-gen-go-http $${REPLY}/;else mkdir -p '$(BIN)';\\\n\tcp ./cmd/kratos/kratos '$(BIN)';cp ./cmd/protoc-gen-go-errors/protoc-gen-go-errors '$(BIN)';cp ./cmd/protoc-gen-go-http/protoc-gen-go-http '$(BIN)'; fi)\nendif\n\t@which protoc-gen-go &> /dev/null || go get google.golang.org/protobuf/cmd/protoc-gen-go\n\t@which protoc-gen-go-grpc &> /dev/null || go get google.golang.org/grpc/cmd/protoc-gen-go-grpc\n\t@which protoc-gen-validate  &> /dev/null || go get github.com/envoyproxy/protoc-gen-validate\n\t@echo \"install finished\"\n\n.PHONY: uninstall\nuninstall:\n\t$(shell for i in `which -a kratos | grep -v '/usr/bin/kratos' 2>/dev/null | sort | uniq`; do read -p \"Press to remove $${i} (y/n): \" REPLY; if [ $${REPLY} = \"y\" ]; then rm -f $${i}; fi; done)\n\t$(shell for i in `which -a protoc-gen-go-grpc | grep -v '/usr/bin/protoc-gen-go-errors' 2>/dev/null | sort | uniq`; do read -p \"Press to remove $${i} (y/n): \" REPLY; if [ $${REPLY} = \"y\" ]; then rm -f $${i}; fi; done)\n\t$(shell for i in `which -a protoc-gen-validate | grep -v '/usr/bin/protoc-gen-go-errors' 2>/dev/null | sort | uniq`; do read -p \"Press to remove $${i} (y/n): \" REPLY; if [ $${REPLY} = \"y\" ]; then rm -f $${i}; fi; done)\n\t@echo \"uninstall finished\"\n\n.PHONY: clean\nclean:\n\t@${TOOLS_SHELL} tidy\n\t@echo \"clean finished\"\n\n.PHONY: fix\nfix: $(LINTER)\n\t@${TOOLS_SHELL} fix\n\t@echo \"lint fix finished\"\n\n.PHONY: test\ntest:\n\t@${TOOLS_SHELL} test\n\t@echo \"go test finished\"\n\n.PHONY: test-coverage\ntest-coverage:\n\t@${TOOLS_SHELL} test_coverage\n\t@echo \"go test with coverage finished\"\n\n.PHONY: lint\nlint: $(LINTER)\n\t@${TOOLS_SHELL} lint\n\t@echo \"lint check finished\"\n\n.PHONY: proto\nproto:\n\tprotoc --proto_path=./api --proto_path=./third_party --go_out=paths=source_relative:./api --go-grpc_out=paths=source_relative:./api --go-http_out=paths=source_relative:./api metadata/metadata.proto\n\tprotoc --proto_path=./third_party --go_out=paths=source_relative:./errors/errors.proto\n"
  },
  {
    "path": "README.md",
    "content": "<p align=\"center\"><a href=\"https://go-kratos.dev/\" target=\"_blank\"><img src=\"https://github.com/go-kratos/kratos/blob/main/docs/images/kratos-large.png?raw=true\"></a></p>\n\n<p align=\"center\">\n<a href=\"https://github.com/go-kratos/kratos/actions\"><img src=\"https://github.com/go-kratos/kratos/workflows/Go/badge.svg\" alt=\"Build Status\"></a>\n<a href=\"https://pkg.go.dev/github.com/go-kratos/kratos/v2\"><img src=\"https://pkg.go.dev/badge/github.com/go-kratos/kratos/v2\" alt=\"GoDoc\"></a>\n<a href=\"https://deepwiki.com/go-kratos/kratos\"><img src=\"https://img.shields.io/badge/DeepWiki-go--kratos%2Fkratos-blue.svg?logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACwAAAAyCAYAAAAnWDnqAAAAAXNSR0IArs4c6QAAA05JREFUaEPtmUtyEzEQhtWTQyQLHNak2AB7ZnyXZMEjXMGeK/AIi+QuHrMnbChYY7MIh8g01fJoopFb0uhhEqqcbWTp06/uv1saEDv4O3n3dV60RfP947Mm9/SQc0ICFQgzfc4CYZoTPAswgSJCCUJUnAAoRHOAUOcATwbmVLWdGoH//PB8mnKqScAhsD0kYP3j/Yt5LPQe2KvcXmGvRHcDnpxfL2zOYJ1mFwrryWTz0advv1Ut4CJgf5uhDuDj5eUcAUoahrdY/56ebRWeraTjMt/00Sh3UDtjgHtQNHwcRGOC98BJEAEymycmYcWwOprTgcB6VZ5JK5TAJ+fXGLBm3FDAmn6oPPjR4rKCAoJCal2eAiQp2x0vxTPB3ALO2CRkwmDy5WohzBDwSEFKRwPbknEggCPB/imwrycgxX2NzoMCHhPkDwqYMr9tRcP5qNrMZHkVnOjRMWwLCcr8ohBVb1OMjxLwGCvjTikrsBOiA6fNyCrm8V1rP93iVPpwaE+gO0SsWmPiXB+jikdf6SizrT5qKasx5j8ABbHpFTx+vFXp9EnYQmLx02h1QTTrl6eDqxLnGjporxl3NL3agEvXdT0WmEost648sQOYAeJS9Q7bfUVoMGnjo4AZdUMQku50McDcMWcBPvr0SzbTAFDfvJqwLzgxwATnCgnp4wDl6Aa+Ax283gghmj+vj7feE2KBBRMW3FzOpLOADl0Isb5587h/U4gGvkt5v60Z1VLG8BhYjbzRwyQZemwAd6cCR5/XFWLYZRIMpX39AR0tjaGGiGzLVyhse5C9RKC6ai42ppWPKiBagOvaYk8lO7DajerabOZP46Lby5wKjw1HCRx7p9sVMOWGzb/vA1hwiWc6jm3MvQDTogQkiqIhJV0nBQBTU+3okKCFDy9WwferkHjtxib7t3xIUQtHxnIwtx4mpg26/HfwVNVDb4oI9RHmx5WGelRVlrtiw43zboCLaxv46AZeB3IlTkwouebTr1y2NjSpHz68WNFjHvupy3q8TFn3Hos2IAk4Ju5dCo8B3wP7VPr/FGaKiG+T+v+TQqIrOqMTL1VdWV1DdmcbO8KXBz6esmYWYKPwDL5b5FA1a0hwapHiom0r/cKaoqr+27/XcrS5UwSMbQAAAABJRU5ErkJggg==\" alt=\"DeepWiki\"></a>\n<!-- DeepWiki badge generated by https://deepwiki.ryoppippi.com/ -->\n<a href=\"https://codecov.io/gh/go-kratos/kratos\"><img src=\"https://codecov.io/gh/go-kratos/kratos/master/graph/badge.svg\" alt=\"codeCov\"></a>\n<a href=\"https://goreportcard.com/report/github.com/go-kratos/kratos\"><img src=\"https://goreportcard.com/badge/github.com/go-kratos/kratos\" alt=\"Go Report Card\"></a>\n<a href=\"https://github.com/go-kratos/kratos/blob/main/LICENSE\"><img src=\"https://img.shields.io/github/license/go-kratos/kratos\" alt=\"License\"></a>\n<a href=\"https://github.com/avelino/awesome-go\"><img src=\"https://awesome.re/mentioned-badge.svg\" alt=\"Awesome Go\"></a>\n<a href=\"https://discord.gg/BWzJsUJ\"><img src=\"https://img.shields.io/discord/766619759214854164?label=chat&logo=discord\" alt=\"Discord\"></a>\n</p>\n<p align=\"center\">\n<a href=\"https://trendshift.io/repositories/3233\" target=\"_blank\"><img src=\"https://trendshift.io/api/badge/repositories/3233\" alt=\"go-kratos%2Fkratos | Trendshift\" style=\"width: 250px; height: 55px;\" width=\"250\" height=\"55\"/></a>\n<a href=\"https://www.producthunt.com/posts/go-kratos?utm_source=badge-featured&utm_medium=badge&utm_souce=badge-go-kratos\" target=\"_blank\"><img src=\"https://api.producthunt.com/widgets/embed-image/v1/featured.svg?post_id=306565&theme=light\" alt=\"Go Kratos - A Go framework for microservices. | Product Hunt\" style=\"width: 250px; height: 54px;\" width=\"250\" height=\"54\" /></a>\n</p>\n\n##### Translate to: [简体中文](README_zh.md)\n\n## About Kratos\n\n> The name is inspired by the Greek-mythology-based game \"God of War\". It tells the adventures of Kratos becoming a god of war from a mortal and launching a god-killing slaughter.\n\nKratos is a microservice-oriented governance framework implemented by golang, which offers convenient capabilities to help you quickly build a bulletproof application from scratch, such as:\n\n- The [communication protocol](https://go-kratos.dev/docs/component/api) is based on the HTTP/gRPC through the definition of Protobuf.\n- Abstract [transport](https://go-kratos.dev/docs/component/transport/overview) layer support: [HTTP](https://go-kratos.dev/docs/component/transport/http) / [gRPC](https://go-kratos.dev/docs/component/transport/grpc).\n- Powerful [middleware](https://go-kratos.dev/docs/component/middleware/overview) design, support: [Tracing (OpenTelemetry)](https://go-kratos.dev/docs/component/middleware/tracing), [Metrics (Prometheus is default)](https://go-kratos.dev/docs/component/middleware/metrics), [Recovery](https://go-kratos.dev/docs/component/middleware/recovery) and more.\n- [Registry](https://go-kratos.dev/docs/component/registry) interface able to be connected with various other centralized registries through plug-ins.\n- The [standard log interfaces](https://go-kratos.dev/docs/component/log) ease the integration of the third-party log libs with logs collected through the *Fluentd*.\n- Automatically support the selection of the content [encoding](https://go-kratos.dev/docs/component/encoding) with Accept and Content-Type.\n- Multiple data sources are supported for [configurations](https://go-kratos.dev/docs/component/config) and dynamic configurations (use atomic operations).\n- In the protocol of HTTP/gRPC, use the uniform [metadata](https://go-kratos.dev/docs/component/metadata) transfer method.\n- You can define [errors](https://go-kratos.dev/docs/component/errors/) in protos and generate enums with protoc-gen-go.\n- You can define [verification rules](https://go-kratos.dev/docs/component/middleware/validate) in Protobuf supported by the HTTP/gRPC service.\n- [Swagger API](https://go-kratos.dev/docs/guide/openapi) is generated Automatically and embed Swagger UI endpoint can be started by adding [Swagger plugin](https://github.com/go-kratos/swagger-api).\n\nKratos is accessible, powerful, and provides tools required for large, robust applications.\n\n## Learning Kratos\n\nKratos has the most extensive and thorough [documentation](https://go-kratos.dev/docs/getting-started/start) and [example](https://github.com/go-kratos/examples) library of all modern web application frameworks, making it a breeze to get started with the framework.\n\nWe also provide a [modern template](https://github.com/go-kratos/kratos-layout). This template should help reduce the work required to set up modern projects.\n\n### Goals\n\nKratos boosts your productivity. With the integration of excellent resources and further support, programmers can get rid of most issues might encounter in the field of distributed systems and software engineering such that they are allowed to focus on the release of businesses only. Additionally, for each programmer, Kratos is also an ideal one learning warehouse for many aspects of microservices to enrich their experiences and skills.\n\n### Principles\n\n* **Simple**: Appropriate design with plain and easy code.\n* **General**: Cover the various utilities for business development.\n* **Highly efficient**: Speeding up the efficiency of businesses upgrading.\n* **Stable**: The base libs validated in the production environment have the characteristics of high testability, high coverage as well as high security and reliability.\n* **Robust**: Eliminating misusing through high quality of the base libs.\n* **High-performance**: Optimal performance excluding the optimization of hacking in case of *unsafe*. \n* **Expandability**: Properly designed interfaces where you can expand utilities such as base libs to meet your further requirements.\n* **Fault-tolerance**: Designed against failure, enhance the understanding and exercising of SRE within Kratos to achieve more robustness.\n* **Toolchain**: Includes an extensive toolchain, such as the code generation of cache, the lint tool, and so forth.\n\n## Getting Started\n\nCreate a kratos playground through [docker](https://www.docker.com/products/docker-desktop):\n\n```shell\ndocker run -it --rm -p 8000:8000 --workdir /workspace golang\n```\n\n```shell\napt-get update && apt-get -y install protobuf-compiler\nexport GOPROXY=https://goproxy.io,direct\ngo install github.com/go-kratos/kratos/cmd/kratos/v2@latest && kratos upgrade\n```\n\n```shell\nkratos new helloworld\ncd helloworld/ && go mod tidy\nkratos run\n```\n\nUse a browser to open and visit: `http://localhost:8000/helloworld/kratos`, The kratos program is running!\n\nIf you need more, please visit the kratos [documentation](https://go-kratos.dev/docs/getting-started/start).\n\n## Security Vulnerabilities\n\nIf you discover a security vulnerability within Kratos, please send an e-mail to tonybase via go-kratos@googlegroups.com. All security vulnerabilities will be promptly addressed.\n\n## Community\n\n- [Wechat Group](https://github.com/go-kratos/kratos/issues/682)\n- [Discord Group](https://discord.gg/BWzJsUJ)\n- [go-kratos.dev](https://go-kratos.dev/en)\n\n## Contributors\n\nThank you for considering contributing to the Kratos framework! The contribution guide can be found in the [Kratos documentation](https://go-kratos.dev/docs/community/contribution).\n\n<a href=\"https://github.com/go-kratos/kratos/graphs/contributors\">\n  <img src=\"https://contrib.rocks/image?repo=go-kratos/kratos\" />\n</a>\n\n## License\n\nThe Kratos framework is open-sourced software licensed under the [MIT license](./LICENSE).\n\n## Acknowledgments\n\nThe following project had particular influence on kratos's design.\n\n- [go-kit/kit](https://github.com/go-kit/kit) is a programming toolkit for building microservices in go.\n- [asim/go-micro](https://github.com/asim/go-micro) a distributed systems development framework.\n- [google/go-cloud](https://github.com/google/go-cloud) is go cloud development kit.\n- [zeromicro/go-zero](https://github.com/zeromicro/go-zero) is a web and rpc framework with lots of builtin engineering practices.\n- [beego/beego](https://github.com/beego/beego) is a web framework including RESTful APIs, web apps and backend services.\n"
  },
  {
    "path": "README_zh.md",
    "content": "<p align=\"center\"><a href=\"https://go-kratos.dev/\" target=\"_blank\"><img src=\"https://github.com/go-kratos/kratos/blob/main/docs/images/kratos-large.png?raw=true\"></a></p>\n\n<p align=\"center\">\n<a href=\"https://github.com/go-kratos/kratos/actions\"><img src=\"https://github.com/go-kratos/kratos/workflows/Go/badge.svg\" alt=\"Build Status\"></a>\n<a href=\"https://pkg.go.dev/github.com/go-kratos/kratos/v2\"><img src=\"https://pkg.go.dev/badge/github.com/go-kratos/kratos/v2\" alt=\"GoDoc\"></a>\n<a href=\"https://codecov.io/gh/go-kratos/kratos\"><img src=\"https://codecov.io/gh/go-kratos/kratos/master/graph/badge.svg\" alt=\"codeCov\"></a>\n<a href=\"https://goreportcard.com/report/github.com/go-kratos/kratos\"><img src=\"https://goreportcard.com/badge/github.com/go-kratos/kratos\" alt=\"Go Report Card\"></a>\n<a href=\"https://github.com/go-kratos/kratos/blob/main/LICENSE\"><img src=\"https://img.shields.io/github/license/go-kratos/kratos\" alt=\"License\"></a>\n<a href=\"https://github.com/avelino/awesome-go\"><img src=\"https://awesome.re/mentioned-badge.svg\" alt=\"Awesome Go\"></a>\n<a href=\"https://discord.gg/BWzJsUJ\"><img src=\"https://img.shields.io/discord/766619759214854164?label=chat&logo=discord\" alt=\"Discord\"></a>\n</p>\n<p align=\"center\">\n<a href=\"https://trendshift.io/repositories/3233\" target=\"_blank\"><img src=\"https://trendshift.io/api/badge/repositories/3233\" alt=\"go-kratos%2Fkratos | Trendshift\" style=\"width: 250px; height: 55px;\" width=\"250\" height=\"55\"/></a>\n<a href=\"https://www.producthunt.com/posts/go-kratos?utm_source=badge-featured&utm_medium=badge&utm_souce=badge-go-kratos\" target=\"_blank\"><img src=\"https://api.producthunt.com/widgets/embed-image/v1/featured.svg?post_id=306565&theme=light\" alt=\"Go Kratos - A Go framework for microservices. | Product Hunt\" style=\"width: 250px; height: 54px;\" width=\"250\" height=\"54\" /></a>\n</p>\n\nTranslations: [English](README.md) | [简体中文](README_zh.md)\n\n# Kratos\n\nKratos 一套轻量级 Go 微服务框架，包含大量微服务相关功能及工具。\n\n> 名字来源于:《战神》游戏以希腊神话为背景，讲述奎托斯（Kratos）由凡人成为战神并展开弑神屠杀的冒险经历。\n\n## Goals\n\n我们致力于提供完整、全面的微服务研发体验，通过整合相关框架和工具，微服务治理部分能够无缝融入整个业务开发周期，使开发者更加专注于业务交付。\n对每位开发者而言，Kratos 是非常不错的学习仓库，可以了解和参考微服务领域的技术积累和经验。\n\n### Principles\n\n* 简单：不过度设计，代码平实简单；\n* 通用：通用业务开发所需要的基础库的功能；\n* 高效：提高业务迭代的效率；\n* 稳定：基础库可测试性高，覆盖率高，有线上实践安全可靠；\n* 健壮：通过良好的基础库设计，减少错用；\n* 高性能：性能高，但不特定为了性能做 hack 优化，引入 unsafe ；\n* 扩展性：良好的接口设计，来扩展实现，或者通过新增基础库目录来扩展功能；\n* 容错性：为失败设计，大量引入对 SRE 的理解，鲁棒性高；\n* 工具链：包含大量工具链，比如 cache 代码生成，lint 工具等等。\n\n## Features\n\n* [APIs](https://go-kratos.dev/zh-cn/docs/component/api) ：协议通信以 HTTP/gRPC 为基础，通过 Protobuf 进行定义；\n* [Errors](https://go-kratos.dev/zh-cn/docs/component/errors/) ：通过 Protobuf 的 Enum 作为错误码定义，以及工具生成判定接口；\n* [Metadata](https://go-kratos.dev/zh-cn/docs/component/metadata) ：在协议通信 HTTP/gRPC 中，通过 Middleware 规范化服务元信息传递；\n* [Config](https://go-kratos.dev/zh-cn/docs/component/config) ：支持多数据源方式，进行配置合并铺平，通过 Atomic 方式支持动态配置；\n* [Logger](https://go-kratos.dev/zh-cn/docs/component/log) ：标准日志接口，可方便集成三方 log 库，并可通过 fluentd 收集日志；\n* [Metrics](https://go-kratos.dev/zh-cn/docs/component/middleware/metrics) ：统一指标接口，可以实现各种指标系统，默认集成 Prometheus；\n* [Tracing](https://go-kratos.dev/zh-cn/docs/component/middleware/tracing) ：遵循 OpenTelemetry 规范定义，以实现微服务链路追踪；\n* [Encoding](https://go-kratos.dev/zh-cn/docs/component/encoding) ：支持 Accept 和 Content-Type 进行自动选择内容编码；\n* [Transport](https://go-kratos.dev/zh-cn/docs/component/transport/overview) ：通用的 [HTTP](https://go-kratos.dev/zh-cn/docs/component/transport/http) /[gRPC](https://go-kratos.dev/zh-cn/docs/component/transport/grpc) 传输层，实现统一的 [Middleware](https://go-kratos.dev/zh-cn/docs/component/middleware/overview) 插件支持；\n* [Registry](https://go-kratos.dev/zh-cn/docs/component/registry) ：实现统一注册中心接口，可插件化对接各种注册中心；\n* [Validation](https://go-kratos.dev/zh-cn/docs/component/middleware/validate): 通过 Protobuf 统一定义校验规则，并同时适用于 HTTP/gRPC 服务；\n* [SwaggerAPI](https://go-kratos.dev/zh-cn/docs/guide/openapi): 通过集成第三方 [Swagger 插件](https://github.com/go-kratos/swagger-api) 能够自动生成 Swagger API 文档并启动内置的 Swagger UI服 务。\n\n## Getting Started\n\n### Required\n\n- [go](https://golang.org/dl/)\n- [protoc](https://github.com/protocolbuffers/protobuf)\n- [protoc-gen-go](https://github.com/protocolbuffers/protobuf-go)\n\n### Installing\n\n##### go install 安装：\n\n```\ngo install github.com/go-kratos/kratos/cmd/kratos/v2@latest\nkratos upgrade\n```\n\n##### 源码编译安装：\n\n```\ngit clone https://github.com/go-kratos/kratos\ncd kratos\nmake install\n```\n\n### Create a service\n\n```\n# 创建项目模板\nkratos new helloworld\n\ncd helloworld\n# 拉取项目依赖\ngo mod download\n\n# 生成 proto 模板\nkratos proto add api/helloworld/helloworld.proto\n# 生成 proto 源码\nkratos proto client api/helloworld/helloworld.proto\n# 生成 server 模板\nkratos proto server api/helloworld/helloworld.proto -t internal/service\n\n# 生成所有 proto 源码、wire 等等\ngo generate ./...\n\n# 运行程序\nkratos run\n```\n\n### Kratos Boot\n\n```\nimport \"github.com/go-kratos/kratos/v2\"\nimport \"github.com/go-kratos/kratos/v2/transport/grpc\"\nimport \"github.com/go-kratos/kratos/v2/transport/http\"\n\nhttpSrv := http.NewServer(http.Address(\":8000\"))\ngrpcSrv := grpc.NewServer(grpc.Address(\":9000\"))\n\napp := kratos.New(\n    kratos.Name(\"kratos\"),\n    kratos.Version(\"latest\"),\n    kratos.Server(httpSrv, grpcSrv),\n)\napp.Run()\n```\n\n## Related\n\n* [Docs](https://go-kratos.dev/)\n* [Examples](https://github.com/go-kratos/examples)\n* [Service Layout](https://github.com/go-kratos/kratos-layout)\n\n## Community\n\n* [Wechat Group](https://github.com/go-kratos/kratos/issues/682)\n* [Discord Group](https://discord.gg/BWzJsUJ)\n* Website:  [go-kratos.dev](https://go-kratos.dev)\n* QQ Group: 716486124\n\n## WeChat Official Account\n\n![kratos](docs/images/wechat.png)\n\n## Conventional commits\n\n提交信息的结构应该如下所示:\n\n```text\n<type>[optional scope]: <description>\n\n[optional body]\n\n[optional footer(s)]\n```\n\n提交信息应按照下面的格式:\n- fix: simply describe the problem that has been fixed\n- feat(log): simple describe of new features\n- deps(examples): simple describe the change of the dependency\n- break(http): simple describe the reasons for breaking change\n\n## Sponsors and Backers\n\n![kratos](docs/images/alipay.png)\n\n## License\n\nKratos is MIT licensed. See the [LICENSE](./LICENSE) file for details.\n"
  },
  {
    "path": "ROADMAP.md",
    "content": "# Kratos\n\nThis document defines the roadmap for Kratos development.\n\n## Features\n- [x] Config\n    - [x] Local Files\n    - [x] K8s ConfigMap\n    - [x] Consul\n    - [x] Etcd\n    - [x] Nacos\n- [x] Registry\n    - [x] Consul\n    - [x] Etcd\n    - [x] K8s\n    - [x] Nacos\n- [x] Encoding\n    - [x] JSON\n    - [x] Protobuf\n- [x] Transport\n    - [x] HTTP\n    - [x] gRPC\n- [x] Middleware\n    - [x] Logging\n    - [x] metrics\n    - [x] recovery\n    - [x] gRPC status\n    - [x] transport tracing\n    - [x] Validator\n    - [x] Authentication\n    - [x] Ratelimit\n    - [x] CircuitBreaker\n- [x] Metrics\n    - [x] Prometheus\n    - [x] DataDog\n- [x] Tracing\n    - [x] HTTP\n        - [x] TLS\n        - [x] Client\n        - [x] Service Registrar\n        - [ ] javascript/typescript clients\n    - [x] gRPC\n        - [x] TLS\n        - [x] Unary Handler\n        - [x] Streaming Handler\n- [ ] Cache\n    - [ ] go-redis\n- [x] Event\n    - [x] Pub/Sub\n    - [x] Kafka\n    - [ ] Nats\n- [x] Database\n    - [x] Ent\n    - [ ] Gorm\n\n## Platform\n- [ ] Kratos API\n    - [ ] Auth\n    - [ ] Config\n    - [ ] Registry\n    - [ ] Events\n- [ ] Kratos Runtime\n    - [ ] Secrets\n    - [ ] Service-to-Service\n    - [ ] Publish and Subscribe\n    - [ ] Observability\n    - [ ] Controllable\n- [ ] Kratos UI\n    - [ ] Auth\n    - [ ] Config\n    - [ ] Services\n    - [ ] Endpoints\n    - [ ] Ratelimit\n    - [ ] CircuitBreaker\n    - [ ] FaultInjection\n    - [ ] TrafficPolicy\n\n## Tools\n- [x] Kratos\n- [x] HTTP Generator\n    - [ ] API YAML\n- [x] Errors Generator\n"
  },
  {
    "path": "SECURITY.md",
    "content": "# Security Policy\n\n## Supported Versions\n\nUse this section to tell people about which versions of your project are\ncurrently being supported with security updates.\n\n| Version     | Supported          |\n|-------------|--------------------|\n| 2.0.rc1     | :white_check_mark: |\n| < 2.0.beta3 | :x:                |\n\n## Reporting a Vulnerability\n\nUse this section to tell people how to report a vulnerability.\n\nTell them where to go, how often they can expect to get an update on a\nreported vulnerability, what to expect if the vulnerability is accepted or\ndeclined, etc.\n"
  },
  {
    "path": "api/README.md",
    "content": "# API proto\n"
  },
  {
    "path": "api/metadata/metadata.pb.go",
    "content": "// Code generated by protoc-gen-go. DO NOT EDIT.\n// versions:\n// \tprotoc-gen-go v1.28.0\n// \tprotoc        v3.19.4\n// source: metadata/metadata.proto\n\npackage metadata\n\nimport (\n\t_ \"google.golang.org/genproto/googleapis/api/annotations\"\n\tprotoreflect \"google.golang.org/protobuf/reflect/protoreflect\"\n\tprotoimpl \"google.golang.org/protobuf/runtime/protoimpl\"\n\tdescriptorpb \"google.golang.org/protobuf/types/descriptorpb\"\n\treflect \"reflect\"\n\tsync \"sync\"\n)\n\nconst (\n\t// Verify that this generated code is sufficiently up-to-date.\n\t_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)\n\t// Verify that runtime/protoimpl is sufficiently up-to-date.\n\t_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)\n)\n\ntype ListServicesRequest struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n}\n\nfunc (x *ListServicesRequest) Reset() {\n\t*x = ListServicesRequest{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_metadata_metadata_proto_msgTypes[0]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *ListServicesRequest) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*ListServicesRequest) ProtoMessage() {}\n\nfunc (x *ListServicesRequest) ProtoReflect() protoreflect.Message {\n\tmi := &file_metadata_metadata_proto_msgTypes[0]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use ListServicesRequest.ProtoReflect.Descriptor instead.\nfunc (*ListServicesRequest) Descriptor() ([]byte, []int) {\n\treturn file_metadata_metadata_proto_rawDescGZIP(), []int{0}\n}\n\ntype ListServicesReply struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\tServices []string `protobuf:\"bytes,1,rep,name=services,proto3\" json:\"services,omitempty\"`\n\tMethods  []string `protobuf:\"bytes,2,rep,name=methods,proto3\" json:\"methods,omitempty\"`\n}\n\nfunc (x *ListServicesReply) Reset() {\n\t*x = ListServicesReply{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_metadata_metadata_proto_msgTypes[1]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *ListServicesReply) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*ListServicesReply) ProtoMessage() {}\n\nfunc (x *ListServicesReply) ProtoReflect() protoreflect.Message {\n\tmi := &file_metadata_metadata_proto_msgTypes[1]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use ListServicesReply.ProtoReflect.Descriptor instead.\nfunc (*ListServicesReply) Descriptor() ([]byte, []int) {\n\treturn file_metadata_metadata_proto_rawDescGZIP(), []int{1}\n}\n\nfunc (x *ListServicesReply) GetServices() []string {\n\tif x != nil {\n\t\treturn x.Services\n\t}\n\treturn nil\n}\n\nfunc (x *ListServicesReply) GetMethods() []string {\n\tif x != nil {\n\t\treturn x.Methods\n\t}\n\treturn nil\n}\n\ntype GetServiceDescRequest struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\tName string `protobuf:\"bytes,1,opt,name=name,proto3\" json:\"name,omitempty\"`\n}\n\nfunc (x *GetServiceDescRequest) Reset() {\n\t*x = GetServiceDescRequest{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_metadata_metadata_proto_msgTypes[2]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *GetServiceDescRequest) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*GetServiceDescRequest) ProtoMessage() {}\n\nfunc (x *GetServiceDescRequest) ProtoReflect() protoreflect.Message {\n\tmi := &file_metadata_metadata_proto_msgTypes[2]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use GetServiceDescRequest.ProtoReflect.Descriptor instead.\nfunc (*GetServiceDescRequest) Descriptor() ([]byte, []int) {\n\treturn file_metadata_metadata_proto_rawDescGZIP(), []int{2}\n}\n\nfunc (x *GetServiceDescRequest) GetName() string {\n\tif x != nil {\n\t\treturn x.Name\n\t}\n\treturn \"\"\n}\n\ntype GetServiceDescReply struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\tFileDescSet *descriptorpb.FileDescriptorSet `protobuf:\"bytes,1,opt,name=file_desc_set,json=fileDescSet,proto3\" json:\"file_desc_set,omitempty\"`\n}\n\nfunc (x *GetServiceDescReply) Reset() {\n\t*x = GetServiceDescReply{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_metadata_metadata_proto_msgTypes[3]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *GetServiceDescReply) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*GetServiceDescReply) ProtoMessage() {}\n\nfunc (x *GetServiceDescReply) ProtoReflect() protoreflect.Message {\n\tmi := &file_metadata_metadata_proto_msgTypes[3]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use GetServiceDescReply.ProtoReflect.Descriptor instead.\nfunc (*GetServiceDescReply) Descriptor() ([]byte, []int) {\n\treturn file_metadata_metadata_proto_rawDescGZIP(), []int{3}\n}\n\nfunc (x *GetServiceDescReply) GetFileDescSet() *descriptorpb.FileDescriptorSet {\n\tif x != nil {\n\t\treturn x.FileDescSet\n\t}\n\treturn nil\n}\n\nvar File_metadata_metadata_proto protoreflect.FileDescriptor\n\nvar file_metadata_metadata_proto_rawDesc = []byte{\n\t0x0a, 0x17, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x2f, 0x6d, 0x65, 0x74, 0x61, 0x64,\n\t0x61, 0x74, 0x61, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0a, 0x6b, 0x72, 0x61, 0x74, 0x6f,\n\t0x73, 0x2e, 0x61, 0x70, 0x69, 0x1a, 0x20, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72,\n\t0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f,\n\t0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1c, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f,\n\t0x61, 0x70, 0x69, 0x2f, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e,\n\t0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x15, 0x0a, 0x13, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x65, 0x72,\n\t0x76, 0x69, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x49, 0x0a, 0x11,\n\t0x4c, 0x69, 0x73, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x52, 0x65, 0x70, 0x6c,\n\t0x79, 0x12, 0x1a, 0x0a, 0x08, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x18, 0x01, 0x20,\n\t0x03, 0x28, 0x09, 0x52, 0x08, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x12, 0x18, 0x0a,\n\t0x07, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07,\n\t0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x73, 0x22, 0x2b, 0x0a, 0x15, 0x47, 0x65, 0x74, 0x53, 0x65,\n\t0x72, 0x76, 0x69, 0x63, 0x65, 0x44, 0x65, 0x73, 0x63, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,\n\t0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04,\n\t0x6e, 0x61, 0x6d, 0x65, 0x22, 0x5d, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69,\n\t0x63, 0x65, 0x44, 0x65, 0x73, 0x63, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x46, 0x0a, 0x0d, 0x66,\n\t0x69, 0x6c, 0x65, 0x5f, 0x64, 0x65, 0x73, 0x63, 0x5f, 0x73, 0x65, 0x74, 0x18, 0x01, 0x20, 0x01,\n\t0x28, 0x0b, 0x32, 0x22, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74,\n\t0x6f, 0x62, 0x75, 0x66, 0x2e, 0x46, 0x69, 0x6c, 0x65, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70,\n\t0x74, 0x6f, 0x72, 0x53, 0x65, 0x74, 0x52, 0x0b, 0x66, 0x69, 0x6c, 0x65, 0x44, 0x65, 0x73, 0x63,\n\t0x53, 0x65, 0x74, 0x32, 0xdd, 0x01, 0x0a, 0x08, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61,\n\t0x12, 0x61, 0x0a, 0x0c, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73,\n\t0x12, 0x1f, 0x2e, 0x6b, 0x72, 0x61, 0x74, 0x6f, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x4c, 0x69,\n\t0x73, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,\n\t0x74, 0x1a, 0x1d, 0x2e, 0x6b, 0x72, 0x61, 0x74, 0x6f, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x4c,\n\t0x69, 0x73, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x52, 0x65, 0x70, 0x6c, 0x79,\n\t0x22, 0x11, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x0b, 0x12, 0x09, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x69,\n\t0x63, 0x65, 0x73, 0x12, 0x6e, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63,\n\t0x65, 0x44, 0x65, 0x73, 0x63, 0x12, 0x21, 0x2e, 0x6b, 0x72, 0x61, 0x74, 0x6f, 0x73, 0x2e, 0x61,\n\t0x70, 0x69, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x44, 0x65, 0x73,\n\t0x63, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x6b, 0x72, 0x61, 0x74, 0x6f,\n\t0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65,\n\t0x44, 0x65, 0x73, 0x63, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x18, 0x82, 0xd3, 0xe4, 0x93, 0x02,\n\t0x12, 0x12, 0x10, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2f, 0x7b, 0x6e, 0x61,\n\t0x6d, 0x65, 0x7d, 0x42, 0x63, 0x0a, 0x15, 0x63, 0x6f, 0x6d, 0x2e, 0x67, 0x69, 0x74, 0x68, 0x75,\n\t0x62, 0x2e, 0x6b, 0x72, 0x61, 0x74, 0x6f, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x50, 0x01, 0x5a, 0x3c,\n\t0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x6f, 0x2d, 0x6b, 0x72,\n\t0x61, 0x74, 0x6f, 0x73, 0x2f, 0x6b, 0x72, 0x61, 0x74, 0x6f, 0x73, 0x2f, 0x76, 0x32, 0x2f, 0x61,\n\t0x70, 0x69, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x6b, 0x72, 0x61, 0x74, 0x6f, 0x73, 0x2f,\n\t0x61, 0x70, 0x69, 0x3b, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xa2, 0x02, 0x09, 0x4b,\n\t0x72, 0x61, 0x74, 0x6f, 0x73, 0x41, 0x50, 0x49, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,\n}\n\nvar (\n\tfile_metadata_metadata_proto_rawDescOnce sync.Once\n\tfile_metadata_metadata_proto_rawDescData = file_metadata_metadata_proto_rawDesc\n)\n\nfunc file_metadata_metadata_proto_rawDescGZIP() []byte {\n\tfile_metadata_metadata_proto_rawDescOnce.Do(func() {\n\t\tfile_metadata_metadata_proto_rawDescData = protoimpl.X.CompressGZIP(file_metadata_metadata_proto_rawDescData)\n\t})\n\treturn file_metadata_metadata_proto_rawDescData\n}\n\nvar file_metadata_metadata_proto_msgTypes = make([]protoimpl.MessageInfo, 4)\nvar file_metadata_metadata_proto_goTypes = []interface{}{\n\t(*ListServicesRequest)(nil),            // 0: kratos.api.ListServicesRequest\n\t(*ListServicesReply)(nil),              // 1: kratos.api.ListServicesReply\n\t(*GetServiceDescRequest)(nil),          // 2: kratos.api.GetServiceDescRequest\n\t(*GetServiceDescReply)(nil),            // 3: kratos.api.GetServiceDescReply\n\t(*descriptorpb.FileDescriptorSet)(nil), // 4: google.protobuf.FileDescriptorSet\n}\nvar file_metadata_metadata_proto_depIdxs = []int32{\n\t4, // 0: kratos.api.GetServiceDescReply.file_desc_set:type_name -> google.protobuf.FileDescriptorSet\n\t0, // 1: kratos.api.Metadata.ListServices:input_type -> kratos.api.ListServicesRequest\n\t2, // 2: kratos.api.Metadata.GetServiceDesc:input_type -> kratos.api.GetServiceDescRequest\n\t1, // 3: kratos.api.Metadata.ListServices:output_type -> kratos.api.ListServicesReply\n\t3, // 4: kratos.api.Metadata.GetServiceDesc:output_type -> kratos.api.GetServiceDescReply\n\t3, // [3:5] is the sub-list for method output_type\n\t1, // [1:3] is the sub-list for method input_type\n\t1, // [1:1] is the sub-list for extension type_name\n\t1, // [1:1] is the sub-list for extension extendee\n\t0, // [0:1] is the sub-list for field type_name\n}\n\nfunc init() { file_metadata_metadata_proto_init() }\nfunc file_metadata_metadata_proto_init() {\n\tif File_metadata_metadata_proto != nil {\n\t\treturn\n\t}\n\tif !protoimpl.UnsafeEnabled {\n\t\tfile_metadata_metadata_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*ListServicesRequest); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_metadata_metadata_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*ListServicesReply); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_metadata_metadata_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*GetServiceDescRequest); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_metadata_metadata_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*GetServiceDescReply); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t}\n\ttype x struct{}\n\tout := protoimpl.TypeBuilder{\n\t\tFile: protoimpl.DescBuilder{\n\t\t\tGoPackagePath: reflect.TypeOf(x{}).PkgPath(),\n\t\t\tRawDescriptor: file_metadata_metadata_proto_rawDesc,\n\t\t\tNumEnums:      0,\n\t\t\tNumMessages:   4,\n\t\t\tNumExtensions: 0,\n\t\t\tNumServices:   1,\n\t\t},\n\t\tGoTypes:           file_metadata_metadata_proto_goTypes,\n\t\tDependencyIndexes: file_metadata_metadata_proto_depIdxs,\n\t\tMessageInfos:      file_metadata_metadata_proto_msgTypes,\n\t}.Build()\n\tFile_metadata_metadata_proto = out.File\n\tfile_metadata_metadata_proto_rawDesc = nil\n\tfile_metadata_metadata_proto_goTypes = nil\n\tfile_metadata_metadata_proto_depIdxs = nil\n}\n"
  },
  {
    "path": "api/metadata/metadata.proto",
    "content": "syntax = \"proto3\";\n\npackage kratos.api;\n\nimport \"google/protobuf/descriptor.proto\";\nimport \"google/api/annotations.proto\";\n\noption go_package = \"github.com/go-kratos/kratos/v2/api/proto/kratos/api;metadata\";\noption java_multiple_files = true;\noption java_package = \"com.github.kratos.api\";\noption objc_class_prefix = \"KratosAPI\";\n\n// Metadata is api definition metadata service.\nservice Metadata {\n  // ListServices list the full name of all services.\n  rpc ListServices (ListServicesRequest) returns (ListServicesReply) {\n    option (google.api.http) = {\n      get: \"/services\",\n    };\n  }\n  // GetServiceDesc get the full fileDescriptorSet of service.\n  rpc GetServiceDesc (GetServiceDescRequest) returns (GetServiceDescReply) {\n    option (google.api.http) = {\n      get: \"/services/{name}\",\n    };\n  }\n}\n\nmessage ListServicesRequest {}\nmessage ListServicesReply {\n  repeated string services = 1;\n  repeated string methods = 2;\n}\n\nmessage GetServiceDescRequest {\n  string name = 1;\n}\n\nmessage GetServiceDescReply {\n  google.protobuf.FileDescriptorSet file_desc_set = 1;\n}\n"
  },
  {
    "path": "api/metadata/metadata_grpc.pb.go",
    "content": "// Code generated by protoc-gen-go-grpc. DO NOT EDIT.\n// versions:\n// - protoc-gen-go-grpc v1.2.0\n// - protoc             v3.19.4\n// source: metadata/metadata.proto\n\npackage metadata\n\nimport (\n\tcontext \"context\"\n\tgrpc \"google.golang.org/grpc\"\n\tcodes \"google.golang.org/grpc/codes\"\n\tstatus \"google.golang.org/grpc/status\"\n)\n\n// This is a compile-time assertion to ensure that this generated file\n// is compatible with the grpc package it is being compiled against.\n// Requires gRPC-Go v1.32.0 or later.\nconst _ = grpc.SupportPackageIsVersion7\n\n// MetadataClient is the client API for Metadata service.\n//\n// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.\ntype MetadataClient interface {\n\t// ListServices list the full name of all services.\n\tListServices(ctx context.Context, in *ListServicesRequest, opts ...grpc.CallOption) (*ListServicesReply, error)\n\t// GetServiceDesc get the full fileDescriptorSet of service.\n\tGetServiceDesc(ctx context.Context, in *GetServiceDescRequest, opts ...grpc.CallOption) (*GetServiceDescReply, error)\n}\n\ntype metadataClient struct {\n\tcc grpc.ClientConnInterface\n}\n\nfunc NewMetadataClient(cc grpc.ClientConnInterface) MetadataClient {\n\treturn &metadataClient{cc}\n}\n\nfunc (c *metadataClient) ListServices(ctx context.Context, in *ListServicesRequest, opts ...grpc.CallOption) (*ListServicesReply, error) {\n\tout := new(ListServicesReply)\n\terr := c.cc.Invoke(ctx, \"/kratos.api.Metadata/ListServices\", in, out, opts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn out, nil\n}\n\nfunc (c *metadataClient) GetServiceDesc(ctx context.Context, in *GetServiceDescRequest, opts ...grpc.CallOption) (*GetServiceDescReply, error) {\n\tout := new(GetServiceDescReply)\n\terr := c.cc.Invoke(ctx, \"/kratos.api.Metadata/GetServiceDesc\", in, out, opts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn out, nil\n}\n\n// MetadataServer is the server API for Metadata service.\n// All implementations must embed UnimplementedMetadataServer\n// for forward compatibility\ntype MetadataServer interface {\n\t// ListServices list the full name of all services.\n\tListServices(context.Context, *ListServicesRequest) (*ListServicesReply, error)\n\t// GetServiceDesc get the full fileDescriptorSet of service.\n\tGetServiceDesc(context.Context, *GetServiceDescRequest) (*GetServiceDescReply, error)\n\tmustEmbedUnimplementedMetadataServer()\n}\n\n// UnimplementedMetadataServer must be embedded to have forward compatible implementations.\ntype UnimplementedMetadataServer struct {\n}\n\nfunc (UnimplementedMetadataServer) ListServices(context.Context, *ListServicesRequest) (*ListServicesReply, error) {\n\treturn nil, status.Errorf(codes.Unimplemented, \"method ListServices not implemented\")\n}\nfunc (UnimplementedMetadataServer) GetServiceDesc(context.Context, *GetServiceDescRequest) (*GetServiceDescReply, error) {\n\treturn nil, status.Errorf(codes.Unimplemented, \"method GetServiceDesc not implemented\")\n}\nfunc (UnimplementedMetadataServer) mustEmbedUnimplementedMetadataServer() {}\n\n// UnsafeMetadataServer may be embedded to opt out of forward compatibility for this service.\n// Use of this interface is not recommended, as added methods to MetadataServer will\n// result in compilation errors.\ntype UnsafeMetadataServer interface {\n\tmustEmbedUnimplementedMetadataServer()\n}\n\nfunc RegisterMetadataServer(s grpc.ServiceRegistrar, srv MetadataServer) {\n\ts.RegisterService(&Metadata_ServiceDesc, srv)\n}\n\nfunc _Metadata_ListServices_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {\n\tin := new(ListServicesRequest)\n\tif err := dec(in); err != nil {\n\t\treturn nil, err\n\t}\n\tif interceptor == nil {\n\t\treturn srv.(MetadataServer).ListServices(ctx, in)\n\t}\n\tinfo := &grpc.UnaryServerInfo{\n\t\tServer:     srv,\n\t\tFullMethod: \"/kratos.api.Metadata/ListServices\",\n\t}\n\thandler := func(ctx context.Context, req interface{}) (interface{}, error) {\n\t\treturn srv.(MetadataServer).ListServices(ctx, req.(*ListServicesRequest))\n\t}\n\treturn interceptor(ctx, in, info, handler)\n}\n\nfunc _Metadata_GetServiceDesc_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {\n\tin := new(GetServiceDescRequest)\n\tif err := dec(in); err != nil {\n\t\treturn nil, err\n\t}\n\tif interceptor == nil {\n\t\treturn srv.(MetadataServer).GetServiceDesc(ctx, in)\n\t}\n\tinfo := &grpc.UnaryServerInfo{\n\t\tServer:     srv,\n\t\tFullMethod: \"/kratos.api.Metadata/GetServiceDesc\",\n\t}\n\thandler := func(ctx context.Context, req interface{}) (interface{}, error) {\n\t\treturn srv.(MetadataServer).GetServiceDesc(ctx, req.(*GetServiceDescRequest))\n\t}\n\treturn interceptor(ctx, in, info, handler)\n}\n\n// Metadata_ServiceDesc is the grpc.ServiceDesc for Metadata service.\n// It's only intended for direct use with grpc.RegisterService,\n// and not to be introspected or modified (even as a copy)\nvar Metadata_ServiceDesc = grpc.ServiceDesc{\n\tServiceName: \"kratos.api.Metadata\",\n\tHandlerType: (*MetadataServer)(nil),\n\tMethods: []grpc.MethodDesc{\n\t\t{\n\t\t\tMethodName: \"ListServices\",\n\t\t\tHandler:    _Metadata_ListServices_Handler,\n\t\t},\n\t\t{\n\t\t\tMethodName: \"GetServiceDesc\",\n\t\t\tHandler:    _Metadata_GetServiceDesc_Handler,\n\t\t},\n\t},\n\tStreams:  []grpc.StreamDesc{},\n\tMetadata: \"metadata/metadata.proto\",\n}\n"
  },
  {
    "path": "api/metadata/metadata_http.pb.go",
    "content": "// Code generated by protoc-gen-go-http. DO NOT EDIT.\n// versions:\n// protoc-gen-go-http v2.3.0\n\npackage metadata\n\nimport (\n\tcontext \"context\"\n\thttp \"github.com/go-kratos/kratos/v2/transport/http\"\n\tbinding \"github.com/go-kratos/kratos/v2/transport/http/binding\"\n)\n\n// This is a compile-time assertion to ensure that this generated file\n// is compatible with the kratos package it is being compiled against.\nvar _ = new(context.Context)\nvar _ = binding.EncodeURL\n\nconst _ = http.SupportPackageIsVersion1\n\ntype MetadataHTTPServer interface {\n\tGetServiceDesc(context.Context, *GetServiceDescRequest) (*GetServiceDescReply, error)\n\tListServices(context.Context, *ListServicesRequest) (*ListServicesReply, error)\n}\n\nfunc RegisterMetadataHTTPServer(s *http.Server, srv MetadataHTTPServer) {\n\tr := s.Route(\"/\")\n\tr.GET(\"/services\", _Metadata_ListServices0_HTTP_Handler(srv))\n\tr.GET(\"/services/{name}\", _Metadata_GetServiceDesc0_HTTP_Handler(srv))\n}\n\nfunc _Metadata_ListServices0_HTTP_Handler(srv MetadataHTTPServer) func(ctx http.Context) error {\n\treturn func(ctx http.Context) error {\n\t\tvar in ListServicesRequest\n\t\tif err := ctx.BindQuery(&in); err != nil {\n\t\t\treturn err\n\t\t}\n\t\thttp.SetOperation(ctx, \"/kratos.api.Metadata/ListServices\")\n\t\th := ctx.Middleware(func(ctx context.Context, req interface{}) (interface{}, error) {\n\t\t\treturn srv.ListServices(ctx, req.(*ListServicesRequest))\n\t\t})\n\t\tout, err := h(ctx, &in)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\treply := out.(*ListServicesReply)\n\t\treturn ctx.Result(200, reply)\n\t}\n}\n\nfunc _Metadata_GetServiceDesc0_HTTP_Handler(srv MetadataHTTPServer) func(ctx http.Context) error {\n\treturn func(ctx http.Context) error {\n\t\tvar in GetServiceDescRequest\n\t\tif err := ctx.BindQuery(&in); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := ctx.BindVars(&in); err != nil {\n\t\t\treturn err\n\t\t}\n\t\thttp.SetOperation(ctx, \"/kratos.api.Metadata/GetServiceDesc\")\n\t\th := ctx.Middleware(func(ctx context.Context, req interface{}) (interface{}, error) {\n\t\t\treturn srv.GetServiceDesc(ctx, req.(*GetServiceDescRequest))\n\t\t})\n\t\tout, err := h(ctx, &in)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\treply := out.(*GetServiceDescReply)\n\t\treturn ctx.Result(200, reply)\n\t}\n}\n\ntype MetadataHTTPClient interface {\n\tGetServiceDesc(ctx context.Context, req *GetServiceDescRequest, opts ...http.CallOption) (rsp *GetServiceDescReply, err error)\n\tListServices(ctx context.Context, req *ListServicesRequest, opts ...http.CallOption) (rsp *ListServicesReply, err error)\n}\n\ntype MetadataHTTPClientImpl struct {\n\tcc *http.Client\n}\n\nfunc NewMetadataHTTPClient(client *http.Client) MetadataHTTPClient {\n\treturn &MetadataHTTPClientImpl{client}\n}\n\nfunc (c *MetadataHTTPClientImpl) GetServiceDesc(ctx context.Context, in *GetServiceDescRequest, opts ...http.CallOption) (*GetServiceDescReply, error) {\n\tvar out GetServiceDescReply\n\tpattern := \"/services/{name}\"\n\tpath := binding.EncodeURL(pattern, in, true)\n\topts = append(opts, http.Operation(\"/kratos.api.Metadata/GetServiceDesc\"))\n\topts = append(opts, http.PathTemplate(pattern))\n\terr := c.cc.Invoke(ctx, \"GET\", path, nil, &out, opts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn &out, err\n}\n\nfunc (c *MetadataHTTPClientImpl) ListServices(ctx context.Context, in *ListServicesRequest, opts ...http.CallOption) (*ListServicesReply, error) {\n\tvar out ListServicesReply\n\tpattern := \"/services\"\n\tpath := binding.EncodeURL(pattern, in, true)\n\topts = append(opts, http.Operation(\"/kratos.api.Metadata/ListServices\"))\n\topts = append(opts, http.PathTemplate(pattern))\n\terr := c.cc.Invoke(ctx, \"GET\", path, nil, &out, opts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn &out, err\n}\n"
  },
  {
    "path": "api/metadata/server.go",
    "content": "package metadata\n\nimport (\n\t\"bytes\"\n\t\"compress/gzip\"\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"sort\"\n\t\"sync\"\n\n\t\"google.golang.org/grpc\"\n\t\"google.golang.org/grpc/codes\"\n\t\"google.golang.org/grpc/status\"\n\t\"google.golang.org/protobuf/proto\"\n\t\"google.golang.org/protobuf/reflect/protodesc\"\n\t\"google.golang.org/protobuf/reflect/protoreflect\"\n\t\"google.golang.org/protobuf/reflect/protoregistry\"\n\tdpb \"google.golang.org/protobuf/types/descriptorpb\"\n\n\t\"github.com/go-kratos/kratos/v2/log\"\n)\n\n// Server is api meta server\ntype Server struct {\n\tUnimplementedMetadataServer\n\n\tsrv      *grpc.Server\n\tlock     sync.Mutex\n\tservices map[string]*dpb.FileDescriptorSet\n\tmethods  map[string][]string\n}\n\n// NewServer create server instance\nfunc NewServer(srv *grpc.Server) *Server {\n\treturn &Server{\n\t\tsrv:      srv,\n\t\tservices: make(map[string]*dpb.FileDescriptorSet),\n\t\tmethods:  make(map[string][]string),\n\t}\n}\n\nfunc (s *Server) load() error {\n\tif len(s.services) > 0 {\n\t\treturn nil\n\t}\n\tif s.srv != nil {\n\t\tfor name, info := range s.srv.GetServiceInfo() {\n\t\t\tfd, err := parseMetadata(info.Metadata)\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"invalid service %s metadata err:%v\", name, err)\n\t\t\t}\n\t\t\tprotoSet, err := allDependency(fd)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\ts.services[name] = &dpb.FileDescriptorSet{File: protoSet}\n\t\t\tfor _, method := range info.Methods {\n\t\t\t\ts.methods[name] = append(s.methods[name], method.Name)\n\t\t\t}\n\t\t}\n\t\treturn nil\n\t}\n\tvar err error\n\tprotoregistry.GlobalFiles.RangeFiles(func(fd protoreflect.FileDescriptor) bool {\n\t\tif fd.Services() == nil {\n\t\t\treturn true\n\t\t}\n\t\tfor i := 0; i < fd.Services().Len(); i++ {\n\t\t\tsvc := fd.Services().Get(i)\n\t\t\tfdp, e := fileDescriptorProto(fd.Path())\n\t\t\tif e != nil {\n\t\t\t\terr = e\n\t\t\t\treturn false\n\t\t\t}\n\t\t\tfdps, e := allDependency(fdp)\n\t\t\tif e != nil {\n\t\t\t\tif errors.Is(e, protoregistry.NotFound) {\n\t\t\t\t\t// Skip this service if one of its dependencies is not found.\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\terr = e\n\t\t\t\treturn false\n\t\t\t}\n\t\t\ts.services[string(svc.FullName())] = &dpb.FileDescriptorSet{File: fdps}\n\t\t\tif svc.Methods() == nil {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tfor j := 0; j < svc.Methods().Len(); j++ {\n\t\t\t\tmethod := svc.Methods().Get(j)\n\t\t\t\ts.methods[string(svc.FullName())] = append(s.methods[string(svc.FullName())], string(method.Name()))\n\t\t\t}\n\t\t}\n\t\treturn true\n\t})\n\treturn err\n}\n\n// ListServices return all services\nfunc (s *Server) ListServices(_ context.Context, _ *ListServicesRequest) (*ListServicesReply, error) {\n\ts.lock.Lock()\n\tdefer s.lock.Unlock()\n\tif err := s.load(); err != nil {\n\t\treturn nil, err\n\t}\n\treply := &ListServicesReply{\n\t\tServices: make([]string, 0, len(s.services)),\n\t\tMethods:  make([]string, 0, len(s.methods)),\n\t}\n\tfor name := range s.services {\n\t\treply.Services = append(reply.Services, name)\n\t}\n\tfor name, methods := range s.methods {\n\t\tfor _, method := range methods {\n\t\t\treply.Methods = append(reply.Methods, fmt.Sprintf(\"/%s/%s\", name, method))\n\t\t}\n\t}\n\tsort.Strings(reply.Services)\n\tsort.Strings(reply.Methods)\n\treturn reply, nil\n}\n\n// GetServiceDesc return service meta by name\nfunc (s *Server) GetServiceDesc(_ context.Context, in *GetServiceDescRequest) (*GetServiceDescReply, error) {\n\ts.lock.Lock()\n\tdefer s.lock.Unlock()\n\tif err := s.load(); err != nil {\n\t\treturn nil, err\n\t}\n\tfds, ok := s.services[in.Name]\n\tif !ok {\n\t\treturn nil, status.Errorf(codes.NotFound, \"service %s not found\", in.Name)\n\t}\n\treturn &GetServiceDescReply{FileDescSet: fds}, nil\n}\n\n// parseMetadata finds the file descriptor bytes specified meta.\n// For SupportPackageIsVersion4, m is the name of the proto file, we\n// call proto.FileDescriptor to get the byte slice.\n// For SupportPackageIsVersion3, m is a byte slice itself.\nfunc parseMetadata(meta any) (*dpb.FileDescriptorProto, error) {\n\t// Check if meta is the file name.\n\tif fileNameForMeta, ok := meta.(string); ok {\n\t\treturn fileDescriptorProto(fileNameForMeta)\n\t}\n\t// Check if meta is the byte slice.\n\tif enc, ok := meta.([]byte); ok {\n\t\treturn decodeFileDesc(enc)\n\t}\n\treturn nil, fmt.Errorf(\"proto does not support metadata: %v\", meta)\n}\n\n// decodeFileDesc does decompression and unmarshalling on the given\n// file descriptor byte slice.\nfunc decodeFileDesc(enc []byte) (*dpb.FileDescriptorProto, error) {\n\traw, err := decompress(enc)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to decompress enc: %v\", err)\n\t}\n\tfd := new(dpb.FileDescriptorProto)\n\tif err := proto.Unmarshal(raw, fd); err != nil {\n\t\treturn nil, fmt.Errorf(\"bad descriptor: %v\", err)\n\t}\n\treturn fd, nil\n}\n\nfunc allDependency(fd *dpb.FileDescriptorProto) ([]*dpb.FileDescriptorProto, error) {\n\tvar files []*dpb.FileDescriptorProto\n\tfor _, dep := range fd.Dependency {\n\t\tfdDep, err := fileDescriptorProto(dep)\n\t\tif err != nil {\n\t\t\tlog.Warnf(\"%s\", err)\n\t\t\tcontinue\n\t\t}\n\t\ttemp, err := allDependency(fdDep)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tfiles = append(files, temp...)\n\t}\n\tfiles = append(files, fd)\n\treturn files, nil\n}\n\n// decompress does gzip decompression.\nfunc decompress(b []byte) ([]byte, error) {\n\tr, err := gzip.NewReader(bytes.NewReader(b))\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"bad gzipped descriptor: %v\", err)\n\t}\n\tout, err := io.ReadAll(r)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"bad gzipped descriptor: %v\", err)\n\t}\n\treturn out, nil\n}\n\nfunc fileDescriptorProto(path string) (*dpb.FileDescriptorProto, error) {\n\tfd, err := protoregistry.GlobalFiles.FindFileByPath(path)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"find proto by path failed, path: %s, err: %s\", path, err)\n\t}\n\tfdpb := protodesc.ToFileDescriptorProto(fd)\n\treturn fdpb, nil\n}\n"
  },
  {
    "path": "app.go",
    "content": "package kratos\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"os\"\n\t\"os/signal\"\n\t\"sync\"\n\t\"syscall\"\n\t\"time\"\n\n\t\"github.com/google/uuid\"\n\t\"golang.org/x/sync/errgroup\"\n\n\t\"github.com/go-kratos/kratos/v2/log\"\n\t\"github.com/go-kratos/kratos/v2/registry\"\n\t\"github.com/go-kratos/kratos/v2/transport\"\n)\n\n// AppInfo is application context value.\ntype AppInfo interface {\n\tID() string\n\tName() string\n\tVersion() string\n\tMetadata() map[string]string\n\tEndpoint() []string\n}\n\n// App is an application components lifecycle manager.\ntype App struct {\n\topts     options\n\tctx      context.Context\n\tcancel   context.CancelFunc\n\tmu       sync.Mutex\n\tinstance *registry.ServiceInstance\n}\n\n// New create an application lifecycle manager.\nfunc New(opts ...Option) *App {\n\to := options{\n\t\tctx:              context.Background(),\n\t\tsigs:             []os.Signal{syscall.SIGTERM, syscall.SIGQUIT, syscall.SIGINT},\n\t\tregistrarTimeout: 10 * time.Second,\n\t}\n\tif id, err := uuid.NewUUID(); err == nil {\n\t\to.id = id.String()\n\t}\n\tfor _, opt := range opts {\n\t\topt(&o)\n\t}\n\tif o.logger != nil {\n\t\tlog.SetLogger(o.logger)\n\t}\n\tctx, cancel := context.WithCancel(o.ctx)\n\treturn &App{\n\t\tctx:    ctx,\n\t\tcancel: cancel,\n\t\topts:   o,\n\t}\n}\n\n// ID returns app instance id.\nfunc (a *App) ID() string { return a.opts.id }\n\n// Name returns service name.\nfunc (a *App) Name() string { return a.opts.name }\n\n// Version returns app version.\nfunc (a *App) Version() string { return a.opts.version }\n\n// Metadata returns service metadata.\nfunc (a *App) Metadata() map[string]string { return a.opts.metadata }\n\n// Endpoint returns endpoints.\nfunc (a *App) Endpoint() []string {\n\tif a.instance != nil {\n\t\treturn a.instance.Endpoints\n\t}\n\treturn nil\n}\n\n// Run executes all OnStart hooks registered with the application's Lifecycle.\nfunc (a *App) Run() error {\n\tinstance, err := a.buildInstance()\n\tif err != nil {\n\t\treturn err\n\t}\n\ta.mu.Lock()\n\ta.instance = instance\n\ta.mu.Unlock()\n\tsctx := NewContext(a.ctx, a)\n\teg, ctx := errgroup.WithContext(sctx)\n\twg := sync.WaitGroup{}\n\n\tfor _, fn := range a.opts.beforeStart {\n\t\tif err = fn(sctx); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\toctx := NewContext(a.opts.ctx, a)\n\tfor _, srv := range a.opts.servers {\n\t\tserver := srv\n\t\teg.Go(func() error {\n\t\t\t<-ctx.Done() // wait for stop signal\n\t\t\tstopCtx := context.WithoutCancel(octx)\n\t\t\tif a.opts.stopTimeout > 0 {\n\t\t\t\tvar cancel context.CancelFunc\n\t\t\t\tstopCtx, cancel = context.WithTimeout(stopCtx, a.opts.stopTimeout)\n\t\t\t\tdefer cancel()\n\t\t\t}\n\t\t\treturn server.Stop(stopCtx)\n\t\t})\n\t\twg.Add(1)\n\t\teg.Go(func() error {\n\t\t\twg.Done() // here is to ensure server start has begun running before register, so defer is not needed\n\t\t\treturn server.Start(octx)\n\t\t})\n\t}\n\twg.Wait()\n\tif a.opts.registrar != nil {\n\t\trctx, rcancel := context.WithTimeout(ctx, a.opts.registrarTimeout)\n\t\tdefer rcancel()\n\t\tif err = a.opts.registrar.Register(rctx, instance); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tfor _, fn := range a.opts.afterStart {\n\t\tif err = fn(sctx); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\tc := make(chan os.Signal, 1)\n\tsignal.Notify(c, a.opts.sigs...)\n\teg.Go(func() error {\n\t\tselect {\n\t\tcase <-ctx.Done():\n\t\t\treturn nil\n\t\tcase <-c:\n\t\t\treturn a.Stop()\n\t\t}\n\t})\n\tif err = eg.Wait(); err != nil && !errors.Is(err, context.Canceled) {\n\t\treturn err\n\t}\n\terr = nil\n\tfor _, fn := range a.opts.afterStop {\n\t\terr = fn(sctx)\n\t}\n\treturn err\n}\n\n// Stop gracefully stops the application.\nfunc (a *App) Stop() (err error) {\n\tsctx := NewContext(a.ctx, a)\n\tfor _, fn := range a.opts.beforeStop {\n\t\terr = fn(sctx)\n\t}\n\n\ta.mu.Lock()\n\tinstance := a.instance\n\ta.mu.Unlock()\n\tif a.opts.registrar != nil && instance != nil {\n\t\tctx, cancel := context.WithTimeout(NewContext(a.ctx, a), a.opts.registrarTimeout)\n\t\tdefer cancel()\n\t\tif err = a.opts.registrar.Deregister(ctx, instance); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tif a.cancel != nil {\n\t\ta.cancel()\n\t}\n\treturn err\n}\n\nfunc (a *App) buildInstance() (*registry.ServiceInstance, error) {\n\tendpoints := make([]string, 0, len(a.opts.endpoints))\n\tfor _, e := range a.opts.endpoints {\n\t\tendpoints = append(endpoints, e.String())\n\t}\n\tif len(endpoints) == 0 {\n\t\tfor _, srv := range a.opts.servers {\n\t\t\tif r, ok := srv.(transport.Endpointer); ok {\n\t\t\t\te, err := r.Endpoint()\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn nil, err\n\t\t\t\t}\n\t\t\t\tendpoints = append(endpoints, e.String())\n\t\t\t}\n\t\t}\n\t}\n\treturn &registry.ServiceInstance{\n\t\tID:        a.opts.id,\n\t\tName:      a.opts.name,\n\t\tVersion:   a.opts.version,\n\t\tMetadata:  a.opts.metadata,\n\t\tEndpoints: endpoints,\n\t}, nil\n}\n\ntype appKey struct{}\n\n// NewContext returns a new Context that carries value.\nfunc NewContext(ctx context.Context, s AppInfo) context.Context {\n\treturn context.WithValue(ctx, appKey{}, s)\n}\n\n// FromContext returns the Transport value stored in ctx, if any.\nfunc FromContext(ctx context.Context) (s AppInfo, ok bool) {\n\ts, ok = ctx.Value(appKey{}).(AppInfo)\n\treturn\n}\n"
  },
  {
    "path": "app_test.go",
    "content": "package kratos\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"net/url\"\n\t\"reflect\"\n\t\"sync\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/go-kratos/kratos/v2/registry\"\n\t\"github.com/go-kratos/kratos/v2/transport/grpc\"\n\t\"github.com/go-kratos/kratos/v2/transport/http\"\n)\n\ntype mockRegistry struct {\n\tlk      sync.Mutex\n\tservice map[string]*registry.ServiceInstance\n}\n\nfunc (r *mockRegistry) Register(_ context.Context, service *registry.ServiceInstance) error {\n\tif service == nil || service.ID == \"\" {\n\t\treturn errors.New(\"no service id\")\n\t}\n\tr.lk.Lock()\n\tdefer r.lk.Unlock()\n\tr.service[service.ID] = service\n\treturn nil\n}\n\n// Deregister the registration.\nfunc (r *mockRegistry) Deregister(_ context.Context, service *registry.ServiceInstance) error {\n\tr.lk.Lock()\n\tdefer r.lk.Unlock()\n\tif r.service[service.ID] == nil {\n\t\treturn errors.New(\"deregister service not found\")\n\t}\n\tdelete(r.service, service.ID)\n\treturn nil\n}\n\nfunc TestApp(t *testing.T) {\n\ths := http.NewServer()\n\tgs := grpc.NewServer()\n\tapp := New(\n\t\tName(\"kratos\"),\n\t\tVersion(\"v1.0.0\"),\n\t\tServer(hs, gs),\n\t\tBeforeStart(func(_ context.Context) error {\n\t\t\tt.Log(\"BeforeStart...\")\n\t\t\treturn nil\n\t\t}),\n\t\tBeforeStop(func(_ context.Context) error {\n\t\t\tt.Log(\"BeforeStop...\")\n\t\t\treturn nil\n\t\t}),\n\t\tAfterStart(func(_ context.Context) error {\n\t\t\tt.Log(\"AfterStart...\")\n\t\t\treturn nil\n\t\t}),\n\t\tAfterStop(func(_ context.Context) error {\n\t\t\tt.Log(\"AfterStop...\")\n\t\t\treturn nil\n\t\t}),\n\t\tRegistrar(&mockRegistry{service: make(map[string]*registry.ServiceInstance)}),\n\t)\n\ttime.AfterFunc(time.Second, func() {\n\t\t_ = app.Stop()\n\t})\n\tif err := app.Run(); err != nil {\n\t\tt.Fatal(err)\n\t}\n}\n\nfunc TestApp_ID(t *testing.T) {\n\tv := \"123\"\n\to := New(ID(v))\n\tif !reflect.DeepEqual(v, o.ID()) {\n\t\tt.Fatalf(\"o.ID():%s is not equal to v:%s\", o.ID(), v)\n\t}\n}\n\nfunc TestApp_Name(t *testing.T) {\n\tv := \"123\"\n\to := New(Name(v))\n\tif !reflect.DeepEqual(v, o.Name()) {\n\t\tt.Fatalf(\"o.Name():%s is not equal to v:%s\", o.Name(), v)\n\t}\n}\n\nfunc TestApp_Version(t *testing.T) {\n\tv := \"123\"\n\to := New(Version(v))\n\tif !reflect.DeepEqual(v, o.Version()) {\n\t\tt.Fatalf(\"o.Version():%s is not equal to v:%s\", o.Version(), v)\n\t}\n}\n\nfunc TestApp_Metadata(t *testing.T) {\n\tv := map[string]string{\n\t\t\"a\": \"1\",\n\t\t\"b\": \"2\",\n\t}\n\to := New(Metadata(v))\n\tif !reflect.DeepEqual(v, o.Metadata()) {\n\t\tt.Fatalf(\"o.Metadata():%s is not equal to v:%s\", o.Metadata(), v)\n\t}\n}\n\nfunc TestApp_Endpoint(t *testing.T) {\n\tv := []string{\"https://go-kratos.dev\", \"localhost\"}\n\tvar endpoints []*url.URL\n\tfor _, urlStr := range v {\n\t\tif endpoint, err := url.Parse(urlStr); err != nil {\n\t\t\tt.Errorf(\"invalid endpoint:%v\", urlStr)\n\t\t} else {\n\t\t\tendpoints = append(endpoints, endpoint)\n\t\t}\n\t}\n\to := New(Endpoint(endpoints...))\n\tif instance, err := o.buildInstance(); err != nil {\n\t\tt.Error(\"build instance failed\")\n\t} else {\n\t\to.instance = instance\n\t}\n\tif !reflect.DeepEqual(o.Endpoint(), v) {\n\t\tt.Errorf(\"Endpoint() = %v, want %v\", o.Endpoint(), v)\n\t}\n}\n\nfunc TestApp_buildInstance(t *testing.T) {\n\twant := struct {\n\t\tid        string\n\t\tname      string\n\t\tversion   string\n\t\tmetadata  map[string]string\n\t\tendpoints []string\n\t}{\n\t\tid:      \"1\",\n\t\tname:    \"kratos\",\n\t\tversion: \"v1.0.0\",\n\t\tmetadata: map[string]string{\n\t\t\t\"a\": \"1\",\n\t\t\t\"b\": \"2\",\n\t\t},\n\t\tendpoints: []string{\"https://go-kratos.dev\", \"localhost\"},\n\t}\n\tvar endpoints []*url.URL\n\tfor _, urlStr := range want.endpoints {\n\t\tif endpoint, err := url.Parse(urlStr); err != nil {\n\t\t\tt.Errorf(\"invalid endpoint:%v\", urlStr)\n\t\t} else {\n\t\t\tendpoints = append(endpoints, endpoint)\n\t\t}\n\t}\n\tapp := New(\n\t\tID(want.id),\n\t\tName(want.name),\n\t\tVersion(want.version),\n\t\tMetadata(want.metadata),\n\t\tEndpoint(endpoints...),\n\t)\n\tif got, err := app.buildInstance(); err != nil {\n\t\tt.Error(\"build got failed\")\n\t} else {\n\t\tif got.ID != want.id {\n\t\t\tt.Errorf(\"ID() = %v, want %v\", got.ID, want.id)\n\t\t}\n\t\tif got.Name != want.name {\n\t\t\tt.Errorf(\"Name() = %v, want %v\", got.Name, want.name)\n\t\t}\n\t\tif got.Version != want.version {\n\t\t\tt.Errorf(\"Version() = %v, want %v\", got.Version, want.version)\n\t\t}\n\t\tif !reflect.DeepEqual(got.Endpoints, want.endpoints) {\n\t\t\tt.Errorf(\"Endpoint() = %v, want %v\", got.Endpoints, want.endpoints)\n\t\t}\n\t\tif !reflect.DeepEqual(got.Metadata, want.metadata) {\n\t\t\tt.Errorf(\"Metadata() = %v, want %v\", got.Metadata, want.metadata)\n\t\t}\n\t}\n}\n\nfunc TestApp_Context(t *testing.T) {\n\ttype fields struct {\n\t\tid       string\n\t\tversion  string\n\t\tname     string\n\t\tinstance *registry.ServiceInstance\n\t\tmetadata map[string]string\n\t\twant     struct {\n\t\t\tid       string\n\t\t\tversion  string\n\t\t\tname     string\n\t\t\tendpoint []string\n\t\t\tmetadata map[string]string\n\t\t}\n\t}\n\ttests := []fields{\n\t\t{\n\t\t\tid:       \"1\",\n\t\t\tname:     \"kratos-v1\",\n\t\t\tinstance: &registry.ServiceInstance{Endpoints: []string{\"https://go-kratos.dev\", \"localhost\"}},\n\t\t\tmetadata: map[string]string{},\n\t\t\tversion:  \"v1\",\n\t\t\twant: struct {\n\t\t\t\tid       string\n\t\t\t\tversion  string\n\t\t\t\tname     string\n\t\t\t\tendpoint []string\n\t\t\t\tmetadata map[string]string\n\t\t\t}{\n\t\t\t\tid: \"1\", version: \"v1\", name: \"kratos-v1\", endpoint: []string{\"https://go-kratos.dev\", \"localhost\"},\n\t\t\t\tmetadata: map[string]string{},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tid:       \"2\",\n\t\t\tname:     \"kratos-v2\",\n\t\t\tinstance: &registry.ServiceInstance{Endpoints: []string{\"test\"}},\n\t\t\tmetadata: map[string]string{\"kratos\": \"https://github.com/go-kratos/kratos\"},\n\t\t\tversion:  \"v2\",\n\t\t\twant: struct {\n\t\t\t\tid       string\n\t\t\t\tversion  string\n\t\t\t\tname     string\n\t\t\t\tendpoint []string\n\t\t\t\tmetadata map[string]string\n\t\t\t}{\n\t\t\t\tid: \"2\", version: \"v2\", name: \"kratos-v2\", endpoint: []string{\"test\"},\n\t\t\t\tmetadata: map[string]string{\"kratos\": \"https://github.com/go-kratos/kratos\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tid:       \"3\",\n\t\t\tname:     \"kratos-v3\",\n\t\t\tinstance: nil,\n\t\t\tmetadata: make(map[string]string),\n\t\t\tversion:  \"v3\",\n\t\t\twant: struct {\n\t\t\t\tid       string\n\t\t\t\tversion  string\n\t\t\t\tname     string\n\t\t\t\tendpoint []string\n\t\t\t\tmetadata map[string]string\n\t\t\t}{\n\t\t\t\tid: \"3\", version: \"v3\", name: \"kratos-v3\", endpoint: nil,\n\t\t\t\tmetadata: map[string]string{},\n\t\t\t},\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\ta := &App{\n\t\t\t\topts:     options{id: tt.id, name: tt.name, metadata: tt.metadata, version: tt.version},\n\t\t\t\tctx:      context.Background(),\n\t\t\t\tcancel:   nil,\n\t\t\t\tinstance: tt.instance,\n\t\t\t}\n\n\t\t\tctx := NewContext(context.Background(), a)\n\n\t\t\tif got, ok := FromContext(ctx); ok {\n\t\t\t\tif got.ID() != tt.want.id {\n\t\t\t\t\tt.Errorf(\"ID() = %v, want %v\", got.ID(), tt.want.id)\n\t\t\t\t}\n\t\t\t\tif got.Name() != tt.want.name {\n\t\t\t\t\tt.Errorf(\"Name() = %v, want %v\", got.Name(), tt.want.name)\n\t\t\t\t}\n\t\t\t\tif got.Version() != tt.want.version {\n\t\t\t\t\tt.Errorf(\"Version() = %v, want %v\", got.Version(), tt.want.version)\n\t\t\t\t}\n\t\t\t\tif !reflect.DeepEqual(got.Endpoint(), tt.want.endpoint) {\n\t\t\t\t\tt.Errorf(\"Endpoint() = %v, want %v\", got.Endpoint(), tt.want.endpoint)\n\t\t\t\t}\n\t\t\t\tif !reflect.DeepEqual(got.Metadata(), tt.want.metadata) {\n\t\t\t\t\tt.Errorf(\"Metadata() = %v, want %v\", got.Metadata(), tt.want.metadata)\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tt.Errorf(\"ok() = %v, want %v\", ok, true)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestApp_ContextCanceled(t *testing.T) {\n\tctx, stop := context.WithCancel(context.Background())\n\tstopFn := func(ctx context.Context) error {\n\t\tselect {\n\t\tcase <-ctx.Done():\n\t\t\tt.Fatal(\"context should not be done yet\")\n\t\tdefault:\n\t\t}\n\t\treturn nil\n\t}\n\tapp := New(Context(ctx), Server(&mockServer{stopFn: stopFn}), StopTimeout(time.Hour))\n\ttime.AfterFunc(time.Millisecond*10, stop)\n\t_ = app.Run()\n}\n"
  },
  {
    "path": "cmd/kratos/go.mod",
    "content": "module github.com/go-kratos/kratos/cmd/kratos/v2\n\ngo 1.23.0\n\ntoolchain go1.24.6\n\nrequire (\n\tgithub.com/AlecAivazis/survey/v2 v2.3.7\n\tgithub.com/charmbracelet/huh v0.8.0\n\tgithub.com/emicklei/proto v1.10.0\n\tgithub.com/fatih/color v1.13.0\n\tgithub.com/spf13/cobra v1.4.0\n\tgolang.org/x/mod v0.17.0\n\tgolang.org/x/text v0.23.0\n)\n\nrequire (\n\tgithub.com/atotto/clipboard v0.1.4 // indirect\n\tgithub.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect\n\tgithub.com/catppuccin/go v0.3.0 // indirect\n\tgithub.com/charmbracelet/bubbles v0.21.1-0.20250623103423-23b8fd6302d7 // indirect\n\tgithub.com/charmbracelet/bubbletea v1.3.6 // indirect\n\tgithub.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc // indirect\n\tgithub.com/charmbracelet/lipgloss v1.1.0 // indirect\n\tgithub.com/charmbracelet/x/ansi v0.9.3 // indirect\n\tgithub.com/charmbracelet/x/cellbuf v0.0.13 // indirect\n\tgithub.com/charmbracelet/x/exp/strings v0.0.0-20240722160745-212f7b056ed0 // indirect\n\tgithub.com/charmbracelet/x/term v0.2.1 // indirect\n\tgithub.com/dustin/go-humanize v1.0.1 // indirect\n\tgithub.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect\n\tgithub.com/inconshreveable/mousetrap v1.0.0 // indirect\n\tgithub.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect\n\tgithub.com/lucasb-eyer/go-colorful v1.2.0 // indirect\n\tgithub.com/mattn/go-colorable v0.1.12 // indirect\n\tgithub.com/mattn/go-isatty v0.0.20 // indirect\n\tgithub.com/mattn/go-localereader v0.0.1 // indirect\n\tgithub.com/mattn/go-runewidth v0.0.16 // indirect\n\tgithub.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b // indirect\n\tgithub.com/mitchellh/hashstructure/v2 v2.0.2 // indirect\n\tgithub.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect\n\tgithub.com/muesli/cancelreader v0.2.2 // indirect\n\tgithub.com/muesli/termenv v0.16.0 // indirect\n\tgithub.com/rivo/uniseg v0.4.7 // indirect\n\tgithub.com/spf13/pflag v1.0.5 // indirect\n\tgithub.com/stretchr/testify v1.7.0 // indirect\n\tgithub.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect\n\tgolang.org/x/sync v0.15.0 // indirect\n\tgolang.org/x/sys v0.33.0 // indirect\n\tgolang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect\n\tgopkg.in/yaml.v3 v3.0.0 // indirect\n)\n"
  },
  {
    "path": "cmd/kratos/go.sum",
    "content": "github.com/AlecAivazis/survey/v2 v2.3.7 h1:6I/u8FvytdGsgonrYsVn2t8t4QiRnh6QSTqkkhIiSjQ=\ngithub.com/AlecAivazis/survey/v2 v2.3.7/go.mod h1:xUTIdE4KCOIjsBAE1JYsUPoCqYdZ1reCfTwbto0Fduo=\ngithub.com/MakeNowJust/heredoc v1.0.0 h1:cXCdzVdstXyiTqTvfqk9SDHpKNjxuom+DOlyEeQ4pzQ=\ngithub.com/MakeNowJust/heredoc v1.0.0/go.mod h1:mG5amYoWBHf8vpLOuehzbGGw0EHxpZZ6lCpQ4fNJ8LE=\ngithub.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2 h1:+vx7roKuyA63nhn5WAunQHLTznkw5W8b1Xc0dNjp83s=\ngithub.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2/go.mod h1:HBCaDeC1lPdgDeDbhX8XFpy1jqjK0IBG8W5K+xYqA0w=\ngithub.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4=\ngithub.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI=\ngithub.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=\ngithub.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8=\ngithub.com/aymanbagabas/go-udiff v0.3.1 h1:LV+qyBQ2pqe0u42ZsUEtPiCaUoqgA9gYRDs3vj1nolY=\ngithub.com/aymanbagabas/go-udiff v0.3.1/go.mod h1:G0fsKmG+P6ylD0r6N/KgQD/nWzgfnl8ZBcNLgcbrw8E=\ngithub.com/catppuccin/go v0.3.0 h1:d+0/YicIq+hSTo5oPuRi5kOpqkVA5tAsU6dNhvRu+aY=\ngithub.com/catppuccin/go v0.3.0/go.mod h1:8IHJuMGaUUjQM82qBrGNBv7LFq6JI3NnQCF6MOlZjpc=\ngithub.com/charmbracelet/bubbles v0.21.1-0.20250623103423-23b8fd6302d7 h1:JFgG/xnwFfbezlUnFMJy0nusZvytYysV4SCS2cYbvws=\ngithub.com/charmbracelet/bubbles v0.21.1-0.20250623103423-23b8fd6302d7/go.mod h1:ISC1gtLcVilLOf23wvTfoQuYbW2q0JevFxPfUzZ9Ybw=\ngithub.com/charmbracelet/bubbletea v1.3.6 h1:VkHIxPJQeDt0aFJIsVxw8BQdh/F/L2KKZGsK6et5taU=\ngithub.com/charmbracelet/bubbletea v1.3.6/go.mod h1:oQD9VCRQFF8KplacJLo28/jofOI2ToOfGYeFgBBxHOc=\ngithub.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc h1:4pZI35227imm7yK2bGPcfpFEmuY1gc2YSTShr4iJBfs=\ngithub.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc/go.mod h1:X4/0JoqgTIPSFcRA/P6INZzIuyqdFY5rm8tb41s9okk=\ngithub.com/charmbracelet/huh v0.8.0 h1:Xz/Pm2h64cXQZn/Jvele4J3r7DDiqFCNIVteYukxDvY=\ngithub.com/charmbracelet/huh v0.8.0/go.mod h1:5YVc+SlZ1IhQALxRPpkGwwEKftN/+OlJlnJYlDRFqN4=\ngithub.com/charmbracelet/lipgloss v1.1.0 h1:vYXsiLHVkK7fp74RkV7b2kq9+zDLoEU4MZoFqR/noCY=\ngithub.com/charmbracelet/lipgloss v1.1.0/go.mod h1:/6Q8FR2o+kj8rz4Dq0zQc3vYf7X+B0binUUBwA0aL30=\ngithub.com/charmbracelet/x/ansi v0.9.3 h1:BXt5DHS/MKF+LjuK4huWrC6NCvHtexww7dMayh6GXd0=\ngithub.com/charmbracelet/x/ansi v0.9.3/go.mod h1:3RQDQ6lDnROptfpWuUVIUG64bD2g2BgntdxH0Ya5TeE=\ngithub.com/charmbracelet/x/cellbuf v0.0.13 h1:/KBBKHuVRbq1lYx5BzEHBAFBP8VcQzJejZ/IA3iR28k=\ngithub.com/charmbracelet/x/cellbuf v0.0.13/go.mod h1:xe0nKWGd3eJgtqZRaN9RjMtK7xUYchjzPr7q6kcvCCs=\ngithub.com/charmbracelet/x/conpty v0.1.0 h1:4zc8KaIcbiL4mghEON8D72agYtSeIgq8FSThSPQIb+U=\ngithub.com/charmbracelet/x/conpty v0.1.0/go.mod h1:rMFsDJoDwVmiYM10aD4bH2XiRgwI7NYJtQgl5yskjEQ=\ngithub.com/charmbracelet/x/errors v0.0.0-20240508181413-e8d8b6e2de86 h1:JSt3B+U9iqk37QUU2Rvb6DSBYRLtWqFqfxf8l5hOZUA=\ngithub.com/charmbracelet/x/errors v0.0.0-20240508181413-e8d8b6e2de86/go.mod h1:2P0UgXMEa6TsToMSuFqKFQR+fZTO9CNGUNokkPatT/0=\ngithub.com/charmbracelet/x/exp/golden v0.0.0-20241011142426-46044092ad91 h1:payRxjMjKgx2PaCWLZ4p3ro9y97+TVLZNaRZgJwSVDQ=\ngithub.com/charmbracelet/x/exp/golden v0.0.0-20241011142426-46044092ad91/go.mod h1:wDlXFlCrmJ8J+swcL/MnGUuYnqgQdW9rhSD61oNMb6U=\ngithub.com/charmbracelet/x/exp/strings v0.0.0-20240722160745-212f7b056ed0 h1:qko3AQ4gK1MTS/de7F5hPGx6/k1u0w4TeYmBFwzYVP4=\ngithub.com/charmbracelet/x/exp/strings v0.0.0-20240722160745-212f7b056ed0/go.mod h1:pBhA0ybfXv6hDjQUZ7hk1lVxBiUbupdw5R31yPUViVQ=\ngithub.com/charmbracelet/x/term v0.2.1 h1:AQeHeLZ1OqSXhrAWpYUtZyX1T3zVxfpZuEQMIQaGIAQ=\ngithub.com/charmbracelet/x/term v0.2.1/go.mod h1:oQ4enTYFV7QN4m0i9mzHrViD7TQKvNEEkHUMCmsxdUg=\ngithub.com/charmbracelet/x/termios v0.1.1 h1:o3Q2bT8eqzGnGPOYheoYS8eEleT5ZVNYNy8JawjaNZY=\ngithub.com/charmbracelet/x/termios v0.1.1/go.mod h1:rB7fnv1TgOPOyyKRJ9o+AsTU/vK5WHJ2ivHeut/Pcwo=\ngithub.com/charmbracelet/x/xpty v0.1.2 h1:Pqmu4TEJ8KeA9uSkISKMU3f+C1F6OGBn8ABuGlqCbtI=\ngithub.com/charmbracelet/x/xpty v0.1.2/go.mod h1:XK2Z0id5rtLWcpeNiMYBccNNBrP2IJnzHI0Lq13Xzq4=\ngithub.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=\ngithub.com/creack/pty v1.1.17/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=\ngithub.com/creack/pty v1.1.24 h1:bJrF4RRfyJnbTJqzRLHzcGaZK1NeM5kTC9jGgovnR1s=\ngithub.com/creack/pty v1.1.24/go.mod h1:08sCNb52WyoAwi2QDyzUCTgcvVFhUzewun7wtTfvcwE=\ngithub.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.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/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=\ngithub.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=\ngithub.com/emicklei/proto v1.10.0 h1:pDGyFRVV5RvV+nkBK9iy3q67FBy9Xa7vwrOTE+g5aGw=\ngithub.com/emicklei/proto v1.10.0/go.mod h1:rn1FgRS/FANiZdD2djyH7TMA9jdRDcYQ9IEN9yvjX0A=\ngithub.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f h1:Y/CXytFA4m6baUTXGLOoWe4PQhGxaX0KpnayAqC48p4=\ngithub.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f/go.mod h1:vw97MGsxSvLiUE2X8qFplwetxpGLQrlU1Q9AUEIzCaM=\ngithub.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w=\ngithub.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=\ngithub.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec h1:qv2VnGeEQHchGaZ/u7lxST/RaJw+cv273q79D81Xbog=\ngithub.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec/go.mod h1:Q48J4R4DvxnHolD5P8pOtXigYlRuPLGl6moFx3ulM68=\ngithub.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=\ngithub.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=\ngithub.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=\ngithub.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=\ngithub.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=\ngithub.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=\ngithub.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=\ngithub.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=\ngithub.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40=\ngithub.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=\ngithub.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=\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.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=\ngithub.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=\ngithub.com/mattn/go-localereader v0.0.1 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2JC/oIi4=\ngithub.com/mattn/go-localereader v0.0.1/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88=\ngithub.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=\ngithub.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=\ngithub.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b h1:j7+1HpAFS1zy5+Q4qx1fWh90gTKwiN4QCGoY9TWyyO4=\ngithub.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=\ngithub.com/mitchellh/hashstructure/v2 v2.0.2 h1:vGKWl0YJqUNxE8d+h8f6NJLcCJrgbhC4NcD46KavDd4=\ngithub.com/mitchellh/hashstructure/v2 v2.0.2/go.mod h1:MG3aRVU/N29oo/V/IhBX8GR/zz4kQkprJgF2EVszyDE=\ngithub.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 h1:ZK8zHtRHOkbHy6Mmr5D264iyp3TiX5OmNcI5cIARiQI=\ngithub.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6/go.mod h1:CJlz5H+gyd6CUWT45Oy4q24RdLyn7Md9Vj2/ldJBSIo=\ngithub.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA=\ngithub.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo=\ngithub.com/muesli/termenv v0.16.0 h1:S5AlUN9dENB57rsbnkPyfdGuWIlkmzJjbFf0Tf5FWUc=\ngithub.com/muesli/termenv v0.16.0/go.mod h1:ZRfOIKPFDYQoDFF4Olj7/QJbW60Ol/kL1pU3VfY/Cnk=\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/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=\ngithub.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=\ngithub.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=\ngithub.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=\ngithub.com/spf13/cobra v1.4.0 h1:y+wJpx64xcgO1V+RcnwW0LEHxTKRi2ZDPSBjWnrg88Q=\ngithub.com/spf13/cobra v1.4.0/go.mod h1:Wo4iy3BUC+X2Fybo0PDqwJIv3dNRiZLHQymsfxlB84g=\ngithub.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=\ngithub.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=\ngithub.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\ngithub.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngithub.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=\ngithub.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngithub.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no=\ngithub.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM=\ngithub.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=\ngolang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=\ngolang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=\ngolang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI=\ngolang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo=\ngolang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=\ngolang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA=\ngolang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=\ngolang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=\ngolang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=\ngolang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.15.0 h1:KWH3jNZsfyT6xfAfKiz6MRNmd46ByHDYaZ7KSkCtdW8=\ngolang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=\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-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/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-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=\ngolang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=\ngolang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=\ngolang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY=\ngolang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=\ngolang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\ngolang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=\ngolang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=\ngolang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=\ngolang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=\ngolang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=\ngolang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=\ngopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\ngopkg.in/yaml.v3 v3.0.0 h1:hjy8E9ON/egN1tAYqKb61G10WtihqetD4sz2H+8nIeA=\ngopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\n"
  },
  {
    "path": "cmd/kratos/internal/base/install.go",
    "content": "package base\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"os/exec\"\n\t\"strings\"\n)\n\n// GoInstall go get path.\nfunc GoInstall(path ...string) error {\n\tfor _, p := range path {\n\t\tif !strings.Contains(p, \"@\") {\n\t\t\tp += \"@latest\"\n\t\t}\n\t\tfmt.Printf(\"go install %s\\n\", p)\n\t\tcmd := exec.Command(\"go\", \"install\", p)\n\t\tcmd.Stdout = os.Stdout\n\t\tcmd.Stderr = os.Stderr\n\t\tif err := cmd.Run(); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "cmd/kratos/internal/base/mod.go",
    "content": "package base\n\nimport (\n\t\"bufio\"\n\t\"bytes\"\n\t\"os\"\n\t\"os/exec\"\n\t\"path/filepath\"\n\t\"strings\"\n\n\t\"golang.org/x/mod/modfile\"\n)\n\n// ModulePath returns go module path.\nfunc ModulePath(filename string) (string, error) {\n\tmodBytes, err := os.ReadFile(filename)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\treturn modfile.ModulePath(modBytes), nil\n}\n\n// ModuleVersion returns module version.\nfunc ModuleVersion(path string) (string, error) {\n\tstdout := &bytes.Buffer{}\n\tfd := exec.Command(\"go\", \"mod\", \"graph\")\n\tfd.Stdout = stdout\n\tfd.Stderr = stdout\n\tif err := fd.Run(); err != nil {\n\t\treturn \"\", err\n\t}\n\trd := bufio.NewReader(stdout)\n\tfor {\n\t\tline, _, err := rd.ReadLine()\n\t\tif err != nil {\n\t\t\treturn \"\", err\n\t\t}\n\t\tstr := string(line)\n\t\ti := strings.Index(str, \"@\")\n\t\tif strings.Contains(str, path+\"@\") && i != -1 {\n\t\t\treturn path + str[i:], nil\n\t\t}\n\t}\n}\n\n// KratosMod returns kratos mod.\nfunc KratosMod() string {\n\t// go 1.15+ read from env GOMODCACHE\n\tcacheOut, _ := exec.Command(\"go\", \"env\", \"GOMODCACHE\").Output()\n\tcachePath := strings.Trim(string(cacheOut), \"\\n\")\n\tpathOut, _ := exec.Command(\"go\", \"env\", \"GOPATH\").Output()\n\tgopath := strings.Trim(string(pathOut), \"\\n\")\n\tif cachePath == \"\" {\n\t\tcachePath = filepath.Join(gopath, \"pkg\", \"mod\")\n\t}\n\tif path, err := ModuleVersion(\"github.com/go-kratos/kratos/v2\"); err == nil {\n\t\t// $GOPATH/pkg/mod/github.com/go-kratos/kratos@v2\n\t\treturn filepath.Join(cachePath, path)\n\t}\n\t// $GOPATH/src/github.com/go-kratos/kratos\n\treturn filepath.Join(gopath, \"src\", \"github.com\", \"go-kratos\", \"kratos\")\n}\n"
  },
  {
    "path": "cmd/kratos/internal/base/mod_test.go",
    "content": "package base\n\nimport (\n\t\"os\"\n\t\"testing\"\n)\n\nfunc TestModuleVersion(t *testing.T) {\n\tv, err := ModuleVersion(\"golang.org/x/mod\")\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tt.Log(v)\n}\n\nfunc TestModulePath(t *testing.T) {\n\tif err := os.Mkdir(\"/tmp/test_mod\", os.ModePerm); err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tf, err := os.Create(\"/tmp/test_mod/go.mod\")\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tmod := `module github.com/go-kratos/kratos/v2\n\ngo 1.21`\n\t_, err = f.WriteString(mod)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tp, err := ModulePath(\"/tmp/test_mod/go.mod\")\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif p != \"github.com/go-kratos/kratos/v2\" {\n\t\tt.Fatalf(\"want: %s, got: %s\", \"github.com/go-kratos/kratos/v2\", p)\n\t}\n\n\tt.Cleanup(func() { os.RemoveAll(\"/tmp/test_mod\") })\n}\n"
  },
  {
    "path": "cmd/kratos/internal/base/path.go",
    "content": "package base\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"log\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"strings\"\n\n\t\"github.com/fatih/color\"\n)\n\nfunc kratosHome() string {\n\tdir, err := os.UserHomeDir()\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\thome := filepath.Join(dir, \".kratos\")\n\tif _, err := os.Stat(home); os.IsNotExist(err) {\n\t\tif err := os.MkdirAll(home, 0o700); err != nil {\n\t\t\tlog.Fatal(err)\n\t\t}\n\t}\n\treturn home\n}\n\nfunc kratosHomeWithDir(dir string) string {\n\thome := filepath.Join(kratosHome(), dir)\n\tif _, err := os.Stat(home); os.IsNotExist(err) {\n\t\tif err := os.MkdirAll(home, 0o700); err != nil {\n\t\t\tlog.Fatal(err)\n\t\t}\n\t}\n\treturn home\n}\n\nfunc copyFile(src, dst string, replaces []string) error {\n\tsrcinfo, err := os.Stat(src)\n\tif err != nil {\n\t\treturn err\n\t}\n\tbuf, err := os.ReadFile(src)\n\tif err != nil {\n\t\treturn err\n\t}\n\tvar old string\n\tfor i, next := range replaces {\n\t\tif i%2 == 0 {\n\t\t\told = next\n\t\t\tcontinue\n\t\t}\n\t\tbuf = bytes.ReplaceAll(buf, []byte(old), []byte(next))\n\t}\n\treturn os.WriteFile(dst, buf, srcinfo.Mode())\n}\n\nfunc copyDir(src, dst string, replaces, ignores []string) error {\n\tsrcinfo, err := os.Stat(src)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\terr = os.MkdirAll(dst, srcinfo.Mode())\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfds, err := os.ReadDir(src)\n\tif err != nil {\n\t\treturn err\n\t}\n\tfor _, fd := range fds {\n\t\tif hasSets(fd.Name(), ignores) {\n\t\t\tcontinue\n\t\t}\n\t\tsrcfp := filepath.Join(src, fd.Name())\n\t\tdstfp := filepath.Join(dst, fd.Name())\n\t\tvar e error\n\t\tif fd.IsDir() {\n\t\t\te = copyDir(srcfp, dstfp, replaces, ignores)\n\t\t} else {\n\t\t\te = copyFile(srcfp, dstfp, replaces)\n\t\t}\n\t\tif e != nil {\n\t\t\treturn e\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc hasSets(name string, sets []string) bool {\n\tfor _, ig := range sets {\n\t\tif ig == name {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\nfunc Tree(path string, dir string) {\n\t_ = filepath.Walk(path, func(path string, info os.FileInfo, err error) error {\n\t\tif err == nil && info != nil && !info.IsDir() {\n\t\t\tfmt.Printf(\"%s %s (%v bytes)\\n\", color.GreenString(\"CREATED\"), strings.ReplaceAll(path, dir+\"/\", \"\"), info.Size())\n\t\t}\n\t\treturn nil\n\t})\n}\n"
  },
  {
    "path": "cmd/kratos/internal/base/repo.go",
    "content": "package base\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"net\"\n\t\"os\"\n\t\"os/exec\"\n\t\"path\"\n\t\"path/filepath\"\n\t\"strings\"\n)\n\nvar unExpandVarPath = []string{\"~\", \".\", \"..\"}\n\n// Repo is git repository manager.\ntype Repo struct {\n\turl    string\n\thome   string\n\tbranch string\n}\n\nfunc repoDir(url string) string {\n\tvcsURL, err := ParseVCSUrl(url)\n\tif err != nil {\n\t\treturn url\n\t}\n\t// check host contains port\n\thost, _, err := net.SplitHostPort(vcsURL.Host)\n\tif err != nil {\n\t\thost = vcsURL.Host\n\t}\n\tfor _, p := range unExpandVarPath {\n\t\thost = strings.TrimLeft(host, p)\n\t}\n\tdir := path.Base(path.Dir(vcsURL.Path))\n\turl = fmt.Sprintf(\"%s/%s\", host, dir)\n\treturn url\n}\n\n// NewRepo new a repository manager.\nfunc NewRepo(url string, branch string) *Repo {\n\treturn &Repo{\n\t\turl:    url,\n\t\thome:   kratosHomeWithDir(\"repo/\" + repoDir(url)),\n\t\tbranch: branch,\n\t}\n}\n\n// Path returns the repository cache path.\nfunc (r *Repo) Path() string {\n\tstart := strings.LastIndex(r.url, \"/\")\n\tend := strings.LastIndex(r.url, \".git\")\n\tif end == -1 {\n\t\tend = len(r.url)\n\t}\n\tvar branch string\n\tif r.branch == \"\" {\n\t\tbranch = \"@main\"\n\t} else {\n\t\tbranch = \"@\" + r.branch\n\t}\n\treturn path.Join(r.home, r.url[start+1:end]+branch)\n}\n\n// Pull fetch the repository from remote url.\nfunc (r *Repo) Pull(ctx context.Context) error {\n\tcmd := exec.CommandContext(ctx, \"git\", \"symbolic-ref\", \"HEAD\")\n\tcmd.Dir = r.Path()\n\t_, err := cmd.CombinedOutput()\n\tif err != nil {\n\t\treturn err\n\t}\n\tcmd = exec.CommandContext(ctx, \"git\", \"pull\")\n\tcmd.Dir = r.Path()\n\tout, err := cmd.CombinedOutput()\n\tfmt.Println(string(out))\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn err\n}\n\n// Clone clones the repository to cache path.\nfunc (r *Repo) Clone(ctx context.Context) error {\n\tif _, err := os.Stat(r.Path()); !os.IsNotExist(err) {\n\t\treturn r.Pull(ctx)\n\t}\n\tvar cmd *exec.Cmd\n\tif r.branch == \"\" {\n\t\tcmd = exec.CommandContext(ctx, \"git\", \"clone\", r.url, r.Path())\n\t} else {\n\t\tcmd = exec.CommandContext(ctx, \"git\", \"clone\", \"-b\", r.branch, r.url, r.Path())\n\t}\n\tout, err := cmd.CombinedOutput()\n\tfmt.Println(string(out))\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\n// CopyTo copies the repository to project path.\nfunc (r *Repo) CopyTo(ctx context.Context, to string, modPath string, ignores []string) error {\n\tif err := r.Clone(ctx); err != nil {\n\t\treturn err\n\t}\n\tmod, err := ModulePath(filepath.Join(r.Path(), \"go.mod\"))\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn copyDir(r.Path(), to, []string{mod, modPath}, ignores)\n}\n\n// CopyToV2 copies the repository to project path\nfunc (r *Repo) CopyToV2(ctx context.Context, to string, modPath string, ignores, replaces []string) error {\n\tif err := r.Clone(ctx); err != nil {\n\t\treturn err\n\t}\n\tmod, err := ModulePath(filepath.Join(r.Path(), \"go.mod\"))\n\tif err != nil {\n\t\treturn err\n\t}\n\treplaces = append([]string{mod, modPath}, replaces...)\n\treturn copyDir(r.Path(), to, replaces, ignores)\n}\n"
  },
  {
    "path": "cmd/kratos/internal/base/repo_test.go",
    "content": "package base\n\nimport (\n\t\"context\"\n\t\"os\"\n\t\"testing\"\n)\n\nfunc TestRepo(t *testing.T) {\n\turls := []string{\n\t\t// ssh://[user@]host.xz[:port]/path/to/repo.git/\n\t\t\"ssh://git@github.com:7875/go-kratos/kratos.git\",\n\t\t// git://host.xz[:port]/path/to/repo.git/\n\t\t\"git://github.com:7875/go-kratos/kratos.git\",\n\t\t// http[s]://host.xz[:port]/path/to/repo.git/\n\t\t\"https://github.com:7875/go-kratos/kratos.git\",\n\t\t// ftp[s]://host.xz[:port]/path/to/repo.git/\n\t\t\"ftps://github.com:7875/go-kratos/kratos.git\",\n\t\t//[user@]host.xz:path/to/repo.git/\n\t\t\"git@github.com:go-kratos/kratos.git\",\n\t\t// ssh://[user@]host.xz[:port]/~[user]/path/to/repo.git/\n\t\t\"ssh://git@github.com:7875/go-kratos/kratos.git\",\n\t\t// git://host.xz[:port]/~[user]/path/to/repo.git/\n\t\t\"git://github.com:7875/go-kratos/kratos.git\",\n\t\t//[user@]host.xz:/~[user]/path/to/repo.git/\n\t\t\"git@github.com:go-kratos/kratos.git\",\n\t\t///path/to/repo.git/\n\t\t\"//github.com/go-kratos/kratos.git\",\n\t\t// file:///path/to/repo.git/\n\t\t\"file://./github.com/go-kratos/kratos.git\",\n\t}\n\tfor _, url := range urls {\n\t\tdir := repoDir(url)\n\t\tif dir != \"github.com/go-kratos\" && dir != \"/go-kratos\" {\n\t\t\tt.Fatal(url, \"repoDir test failed\", dir)\n\t\t}\n\t}\n}\n\nfunc TestRepoClone(t *testing.T) {\n\tr := NewRepo(\"https://github.com/go-kratos/service-layout.git\", \"\")\n\tif err := r.Clone(context.Background()); err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif err := r.CopyTo(context.Background(), \"/tmp/test_repo\", \"github.com/go-kratos/kratos-layout\", nil); err != nil {\n\t\tt.Fatal(err)\n\t}\n\tt.Cleanup(func() {\n\t\tos.RemoveAll(\"/tmp/test_repo\")\n\t})\n}\n"
  },
  {
    "path": "cmd/kratos/internal/base/vcs_url.go",
    "content": "package base\n\nimport (\n\t\"errors\"\n\t\"net/url\"\n\t\"regexp\"\n\t\"strings\"\n)\n\nvar (\n\tscpSyntaxRe = regexp.MustCompile(`^(\\w+)@([\\w.-]+):(.*)$`)\n\tscheme      = []string{\"git\", \"https\", \"http\", \"git+ssh\", \"ssh\", \"file\", \"ftp\", \"ftps\"}\n)\n\n// ParseVCSUrl ref https://github.com/golang/go/blob/master/src/cmd/go/internal/vcs/vcs.go\n// see https://go-review.googlesource.com/c/go/+/12226/\n// git url define https://git-scm.com/docs/git-clone#_git_urls\nfunc ParseVCSUrl(repo string) (*url.URL, error) {\n\tvar (\n\t\trepoURL *url.URL\n\t\terr     error\n\t)\n\n\tif m := scpSyntaxRe.FindStringSubmatch(repo); m != nil {\n\t\t// Match SCP-like syntax and convert it to a URL.\n\t\t// Eg, \"git@github.com:user/repo\" becomes\n\t\t// \"ssh://git@github.com/user/repo\".\n\t\trepoURL = &url.URL{\n\t\t\tScheme: \"ssh\",\n\t\t\tUser:   url.User(m[1]),\n\t\t\tHost:   m[2],\n\t\t\tPath:   m[3],\n\t\t}\n\t} else {\n\t\tif !strings.Contains(repo, \"//\") {\n\t\t\trepo = \"//\" + repo\n\t\t}\n\t\tif strings.HasPrefix(repo, \"//git@\") {\n\t\t\trepo = \"ssh:\" + repo\n\t\t} else if strings.HasPrefix(repo, \"//\") {\n\t\t\trepo = \"https:\" + repo\n\t\t}\n\t\trepoURL, err = url.Parse(repo)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\n\t// Iterate over insecure schemes too, because this function simply\n\t// reports the state of the repo. If we can't see insecure schemes then\n\t// we can't report the actual repo URL.\n\tfor _, s := range scheme {\n\t\tif repoURL.Scheme == s {\n\t\t\treturn repoURL, nil\n\t\t}\n\t}\n\treturn nil, errors.New(\"unable to parse repo url\")\n}\n"
  },
  {
    "path": "cmd/kratos/internal/base/vcs_url_test.go",
    "content": "package base\n\nimport (\n\t\"net\"\n\t\"strings\"\n\t\"testing\"\n)\n\nfunc TestParseVCSUrl(t *testing.T) {\n\trepos := []string{\n\t\t// ssh://[user@]host.xz[:port]/path/to/repo.git/\n\t\t\"ssh://git@github.com:7875/go-kratos/kratos.git\",\n\t\t// git://host.xz[:port]/path/to/repo.git/\n\t\t\"git://github.com:7875/go-kratos/kratos.git\",\n\t\t// http[s]://host.xz[:port]/path/to/repo.git/\n\t\t\"https://github.com:7875/go-kratos/kratos.git\",\n\t\t// ftp[s]://host.xz[:port]/path/to/repo.git/\n\t\t\"ftps://github.com:7875/go-kratos/kratos.git\",\n\t\t//[user@]host.xz:path/to/repo.git/\n\t\t\"git@github.com:go-kratos/kratos.git\",\n\t\t// ssh://[user@]host.xz[:port]/~[user]/path/to/repo.git/\n\t\t\"ssh://git@github.com:7875/go-kratos/kratos.git\",\n\t\t// git://host.xz[:port]/~[user]/path/to/repo.git/\n\t\t\"git://github.com:7875/go-kratos/kratos.git\",\n\t\t//[user@]host.xz:/~[user]/path/to/repo.git/\n\t\t\"git@github.com:go-kratos/kratos.git\",\n\t\t///path/to/repo.git/\n\t\t\"~/go-kratos/kratos.git\",\n\t\t// file:///path/to/repo.git/\n\t\t\"file://~/go-kratos/kratos.git\",\n\t}\n\tfor _, repo := range repos {\n\t\turl, err := ParseVCSUrl(repo)\n\t\tif err != nil {\n\t\t\tt.Fatal(repo, err)\n\t\t}\n\t\turlPath := strings.TrimLeft(url.Path, \"/\")\n\t\tif urlPath != \"go-kratos/kratos.git\" {\n\t\t\tt.Fatal(repo, \"parse url failed\", urlPath)\n\t\t}\n\t}\n}\n\nfunc TestParseSsh(t *testing.T) {\n\trepo := \"ssh://git@github.com:7875/go-kratos/kratos.git\"\n\turl, err := ParseVCSUrl(repo)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\thost, _, err := net.SplitHostPort(url.Host)\n\tif err != nil {\n\t\thost = url.Host\n\t}\n\tt.Log(host, url.Path)\n}\n"
  },
  {
    "path": "cmd/kratos/internal/change/change.go",
    "content": "package change\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\n\t\"github.com/spf13/cobra\"\n)\n\n// CmdChange is kratos change log tool\nvar CmdChange = &cobra.Command{\n\tUse:   \"changelog\",\n\tShort: \"Get a kratos change log\",\n\tLong:  \"Get a kratos release or commits info. Example: kratos changelog dev or kratos changelog {version}\",\n\tRun:   run,\n}\n\nvar (\n\ttoken   string\n\trepoURL string\n)\n\nfunc init() {\n\tif repoURL = os.Getenv(\"KRATOS_REPO\"); repoURL == \"\" {\n\t\trepoURL = \"https://github.com/go-kratos/kratos.git\"\n\t}\n\tCmdChange.Flags().StringVarP(&repoURL, \"repo-url\", \"r\", repoURL, \"github repo\")\n\ttoken = os.Getenv(\"GITHUB_TOKEN\")\n}\n\nfunc run(_ *cobra.Command, args []string) {\n\towner, repo := ParseGithubURL(repoURL)\n\tapi := GithubAPI{Owner: owner, Repo: repo, Token: token}\n\tversion := \"latest\"\n\tif len(args) > 0 {\n\t\tversion = args[0]\n\t}\n\tif version == \"dev\" {\n\t\tinfo := api.GetCommitsInfo()\n\t\tfmt.Print(ParseCommitsInfo(info))\n\t\treturn\n\t}\n\tinfo := api.GetReleaseInfo(version)\n\tfmt.Print(ParseReleaseInfo(info))\n}\n"
  },
  {
    "path": "cmd/kratos/internal/change/get.go",
    "content": "package change\n\nimport (\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"os\"\n\t\"regexp\"\n\t\"strings\"\n\t\"time\"\n)\n\ntype ReleaseInfo struct {\n\tAuthor struct {\n\t\tLogin string `json:\"login\"`\n\t} `json:\"author\"`\n\tPublishedAt string `json:\"published_at\"`\n\tBody        string `json:\"body\"`\n\tHTMLURL     string `json:\"html_url\"`\n}\n\ntype CommitInfo struct {\n\tCommit struct {\n\t\tMessage string `json:\"message\"`\n\t} `json:\"commit\"`\n}\n\ntype ErrorInfo struct {\n\tMessage string\n}\n\ntype GithubAPI struct {\n\tOwner string\n\tRepo  string\n\tToken string\n}\n\n// GetReleaseInfo for getting kratos release info.\nfunc (g *GithubAPI) GetReleaseInfo(version string) ReleaseInfo {\n\tapi := fmt.Sprintf(\"https://api.github.com/repos/%s/%s/releases/latest\", g.Owner, g.Repo)\n\tif version != \"latest\" {\n\t\tapi = fmt.Sprintf(\"https://api.github.com/repos/%s/%s/releases/tags/%s\", g.Owner, g.Repo, version)\n\t}\n\tresp, code := requestGithubAPI(api, http.MethodGet, nil, g.Token)\n\tif code != http.StatusOK {\n\t\tprintGithubErrorInfo(resp)\n\t}\n\treleaseInfo := ReleaseInfo{}\n\terr := json.Unmarshal(resp, &releaseInfo)\n\tif err != nil {\n\t\tfatal(err)\n\t}\n\treturn releaseInfo\n}\n\n// GetCommitsInfo for getting kratos commits info.\nfunc (g *GithubAPI) GetCommitsInfo() []CommitInfo {\n\tinfo := g.GetReleaseInfo(\"latest\")\n\tpage := 1\n\tprePage := 100\n\tvar list []CommitInfo\n\tfor {\n\t\turl := fmt.Sprintf(\"https://api.github.com/repos/%s/%s/commits?pre_page=%d&page=%d&since=%s\", g.Owner, g.Repo, prePage, page, info.PublishedAt)\n\t\tresp, code := requestGithubAPI(url, http.MethodGet, nil, g.Token)\n\t\tif code != http.StatusOK {\n\t\t\tprintGithubErrorInfo(resp)\n\t\t}\n\t\tvar res []CommitInfo\n\t\terr := json.Unmarshal(resp, &res)\n\t\tif err != nil {\n\t\t\tfatal(err)\n\t\t}\n\t\tlist = append(list, res...)\n\t\tif len(res) < prePage {\n\t\t\tbreak\n\t\t}\n\t\tpage++\n\t}\n\treturn list\n}\n\nfunc printGithubErrorInfo(body []byte) {\n\terrorInfo := &ErrorInfo{}\n\terr := json.Unmarshal(body, errorInfo)\n\tif err != nil {\n\t\tfatal(err)\n\t}\n\tfatal(errors.New(errorInfo.Message))\n}\n\nfunc requestGithubAPI(url string, method string, body io.Reader, token string) ([]byte, int) {\n\tcli := &http.Client{Timeout: 60 * time.Second}\n\trequest, err := http.NewRequest(method, url, body)\n\tif err != nil {\n\t\tfatal(err)\n\t}\n\tif token != \"\" {\n\t\trequest.Header.Add(\"Authorization\", token)\n\t}\n\tresp, err := cli.Do(request)\n\tif err != nil {\n\t\tfatal(err)\n\t}\n\tdefer resp.Body.Close()\n\tresBody, err := io.ReadAll(resp.Body)\n\tif err != nil {\n\t\tfatal(err)\n\t}\n\treturn resBody, resp.StatusCode\n}\n\nfunc ParseCommitsInfo(info []CommitInfo) string {\n\tgroup := map[string][]string{\n\t\t\"fix\":   {},\n\t\t\"feat\":  {},\n\t\t\"deps\":  {},\n\t\t\"build\": {},\n\t\t\"break\": {},\n\t\t\"chore\": {},\n\t\t\"other\": {},\n\t}\n\n\tfor _, commitInfo := range info {\n\t\tmsg := commitInfo.Commit.Message\n\t\tindex := strings.Index(fmt.Sprintf(\"%q\", msg), `\\n`)\n\t\tif index != -1 {\n\t\t\tmsg = msg[:index-1]\n\t\t}\n\t\tprefix := []string{\"fix\", \"feat\", \"build\", \"deps\", \"break\", \"chore\"}\n\t\tvar matched bool\n\t\tfor _, v := range prefix {\n\t\t\tmsg = strings.TrimPrefix(msg, \" \")\n\t\t\tif strings.HasPrefix(msg, v) {\n\t\t\t\tgroup[v] = append(group[v], msg)\n\t\t\t\tmatched = true\n\t\t\t}\n\t\t}\n\t\tif !matched {\n\t\t\tgroup[\"other\"] = append(group[\"other\"], msg)\n\t\t}\n\t}\n\n\tmd := make(map[string]string)\n\tfor key, value := range group {\n\t\tvar text string\n\t\tswitch key {\n\t\tcase \"break\":\n\t\t\ttext = \"### Breaking Changes\\n\"\n\t\tcase \"deps\":\n\t\t\ttext = \"### Dependencies\\n\"\n\t\tcase \"feat\":\n\t\t\ttext = \"### New Features\\n\"\n\t\tcase \"fix\":\n\t\t\ttext = \"### Bug Fixes\\n\"\n\t\tcase \"build\":\n\t\t\ttext = \"### Builds\\n\"\n\t\tcase \"chore\":\n\t\t\ttext = \"### Chores\\n\"\n\t\tcase \"other\":\n\t\t\ttext = \"### Others\\n\"\n\t\t}\n\t\tif len(value) == 0 {\n\t\t\tcontinue\n\t\t}\n\t\tmd[key] += text\n\t\tfor _, value := range value {\n\t\t\tmd[key] += fmt.Sprintf(\"- %s\\n\", value)\n\t\t}\n\t}\n\treturn fmt.Sprint(md[\"break\"], md[\"deps\"], md[\"feat\"], md[\"fix\"], md[\"build\"], md[\"chore\"], md[\"other\"])\n}\n\nfunc ParseReleaseInfo(info ReleaseInfo) string {\n\treg := regexp.MustCompile(`(?m)^\\s*$[\\r\\n]*|[\\r\\n]+\\s+\\z|<[\\S\\s]+?>`)\n\tbody := reg.ReplaceAll([]byte(info.Body), []byte(\"\"))\n\tif string(body) == \"\" {\n\t\tbody = []byte(\"no release info\")\n\t}\n\tsplitters := \"--------------------------------------------\"\n\treturn fmt.Sprintf(\n\t\t\"Author: %s\\nDate: %s\\nUrl: %s\\n\\n%s\\n\\n%s\\n\\n%s\\n\",\n\t\tinfo.Author.Login,\n\t\tinfo.PublishedAt,\n\t\tinfo.HTMLURL,\n\t\tsplitters,\n\t\tbody,\n\t\tsplitters,\n\t)\n}\n\nfunc ParseGithubURL(url string) (owner string, repo string) {\n\tvar start int\n\tstart = strings.Index(url, \"//\")\n\tif start == -1 {\n\t\tstart = strings.Index(url, \":\") + 1\n\t} else {\n\t\tstart += 2\n\t}\n\tend := strings.LastIndex(url, \"/\")\n\tgitIndex := strings.LastIndex(url, \".git\")\n\tif gitIndex == -1 {\n\t\trepo = url[strings.LastIndex(url, \"/\")+1:]\n\t} else {\n\t\trepo = url[strings.LastIndex(url, \"/\")+1 : gitIndex]\n\t}\n\ttmp := url[start:end]\n\towner = tmp[strings.Index(tmp, \"/\")+1:]\n\treturn\n}\n\nfunc fatal(err error) {\n\tfmt.Fprintf(os.Stderr, \"\\033[31mERROR: %s\\033[m\\n\", err)\n\tos.Exit(1)\n}\n"
  },
  {
    "path": "cmd/kratos/internal/change/get_test.go",
    "content": "package change\n\nimport \"testing\"\n\nfunc TestParseGithubURL(t *testing.T) {\n\turls := []struct {\n\t\turl   string\n\t\towner string\n\t\trepo  string\n\t}{\n\t\t{\"https://github.com/go-kratos/kratos.git\", \"go-kratos\", \"kratos\"},\n\t\t{\"https://github.com/go-kratos/kratos\", \"go-kratos\", \"kratos\"},\n\t\t{\"git@github.com:go-kratos/kratos.git\", \"go-kratos\", \"kratos\"},\n\t\t{\"https://github.com/go-kratos/go-kratos.dev.git\", \"go-kratos\", \"go-kratos.dev\"},\n\t}\n\tfor _, url := range urls {\n\t\towner, repo := ParseGithubURL(url.url)\n\t\tif owner != url.owner {\n\t\t\tt.Fatalf(\"owner want: %s, got: %s\", owner, url.owner)\n\t\t}\n\t\tif repo != url.repo {\n\t\t\tt.Fatalf(\"repo want: %s, got: %s\", repo, url.repo)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "cmd/kratos/internal/project/add.go",
    "content": "package project\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"os\"\n\t\"path/filepath\"\n\n\t\"github.com/AlecAivazis/survey/v2\"\n\t\"github.com/fatih/color\"\n\n\t\"github.com/go-kratos/kratos/cmd/kratos/v2/internal/base\"\n)\n\nvar repoAddIgnores = []string{\n\t\".git\", \".github\", \"api\", \"README.md\", \"LICENSE\", \"go.mod\", \"go.sum\", \"third_party\", \"openapi.yaml\", \".gitignore\",\n}\n\nfunc (p *Project) Add(ctx context.Context, dir string, layout string, branch string, mod string, pkgPath string) error {\n\tto := filepath.Join(dir, p.Name)\n\n\tif _, err := os.Stat(to); !os.IsNotExist(err) {\n\t\tfmt.Printf(\"🚫 %s already exists\\n\", p.Name)\n\t\toverride := false\n\t\tprompt := &survey.Confirm{\n\t\t\tMessage: \"📂 Do you want to override the folder ?\",\n\t\t\tHelp:    \"Delete the existing folder and create the project.\",\n\t\t}\n\t\te := survey.AskOne(prompt, &override)\n\t\tif e != nil {\n\t\t\treturn e\n\t\t}\n\t\tif !override {\n\t\t\treturn err\n\t\t}\n\t\tos.RemoveAll(to)\n\t}\n\n\tfmt.Printf(\"🚀 Add service %s, layout repo is %s, please wait a moment.\\n\\n\", p.Name, layout)\n\n\tpkgPath = fmt.Sprintf(\"%s/%s\", mod, pkgPath)\n\trepo := base.NewRepo(layout, branch)\n\terr := repo.CopyToV2(ctx, to, pkgPath, repoAddIgnores, []string{filepath.Join(p.Path, \"api\"), \"api\"})\n\tif err != nil {\n\t\treturn err\n\t}\n\n\te := os.Rename(\n\t\tfilepath.Join(to, \"cmd\", \"server\"),\n\t\tfilepath.Join(to, \"cmd\", p.Name),\n\t)\n\tif e != nil {\n\t\tif !os.IsNotExist(e) {\n\t\t\treturn e\n\t\t}\n\t}\n\n\tbase.Tree(to, dir)\n\n\tfmt.Printf(\"\\n🍺 Repository creation succeeded %s\\n\", color.GreenString(p.Name))\n\tfmt.Print(\"💻 Use the following command to add a project 👇:\\n\\n\")\n\n\tfmt.Println(color.WhiteString(\"$ cd %s\", p.Name))\n\tfmt.Println(color.WhiteString(\"$ go generate ./...\"))\n\tfmt.Println(color.WhiteString(\"$ go build -o ./bin/ ./... \"))\n\tfmt.Println(color.WhiteString(\"$ ./bin/%s -conf ./configs\\n\", p.Name))\n\tfmt.Println(\"\t\t\t🤝 Thanks for using Kratos\")\n\tfmt.Println(\"\t📚 Tutorial: https://go-kratos.dev/docs/getting-started/start\")\n\treturn nil\n}\n"
  },
  {
    "path": "cmd/kratos/internal/project/new.go",
    "content": "package project\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"os\"\n\t\"path/filepath\"\n\n\t\"github.com/AlecAivazis/survey/v2\"\n\t\"github.com/fatih/color\"\n\n\t\"github.com/go-kratos/kratos/cmd/kratos/v2/internal/base\"\n)\n\n// Project is a project template.\ntype Project struct {\n\tName string\n\tPath string\n}\n\n// New new a project from remote repo.\nfunc (p *Project) New(ctx context.Context, dir string, layout string, branch string) error {\n\tto := filepath.Join(dir, p.Name)\n\tif _, err := os.Stat(to); !os.IsNotExist(err) {\n\t\tfmt.Printf(\"🚫 %s already exists\\n\", p.Name)\n\t\tprompt := &survey.Confirm{\n\t\t\tMessage: \"📂 Do you want to override the folder ?\",\n\t\t\tHelp:    \"Delete the existing folder and create the project.\",\n\t\t}\n\t\tvar override bool\n\t\te := survey.AskOne(prompt, &override)\n\t\tif e != nil {\n\t\t\treturn e\n\t\t}\n\t\tif !override {\n\t\t\treturn err\n\t\t}\n\t\tos.RemoveAll(to)\n\t}\n\tfmt.Printf(\"🚀 Creating service %s, layout repo is %s, please wait a moment.\\n\\n\", p.Name, layout)\n\trepo := base.NewRepo(layout, branch)\n\tif err := repo.CopyTo(ctx, to, p.Name, []string{\".git\", \".github\"}); err != nil {\n\t\treturn err\n\t}\n\te := os.Rename(\n\t\tfilepath.Join(to, \"cmd\", \"server\"),\n\t\tfilepath.Join(to, \"cmd\", p.Name),\n\t)\n\tif e != nil {\n\t\tif !os.IsNotExist(e) {\n\t\t\treturn e\n\t\t}\n\t}\n\tbase.Tree(to, dir)\n\n\tfmt.Printf(\"\\n🍺 Project creation succeeded %s\\n\", color.GreenString(p.Name))\n\tfmt.Print(\"💻 Use the following command to start the project 👇:\\n\\n\")\n\n\tfmt.Println(color.WhiteString(\"$ cd %s\", p.Name))\n\tfmt.Println(color.WhiteString(\"$ go generate ./...\"))\n\tfmt.Println(color.WhiteString(\"$ go build -o ./bin/ ./... \"))\n\tfmt.Println(color.WhiteString(\"$ ./bin/%s -conf ./configs\\n\", p.Name))\n\tfmt.Println(\"\t\t\t🤝 Thanks for using Kratos\")\n\tfmt.Println(\"\t📚 Tutorial: https://go-kratos.dev/docs/getting-started/start\")\n\treturn nil\n}\n"
  },
  {
    "path": "cmd/kratos/internal/project/project.go",
    "content": "package project\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/AlecAivazis/survey/v2\"\n\t\"github.com/charmbracelet/huh\"\n\t\"github.com/spf13/cobra\"\n\n\t\"github.com/go-kratos/kratos/cmd/kratos/v2/internal/base\"\n)\n\nvar projects = map[string]string{\n\t\"service\": \"https://github.com/go-kratos/kratos-layout.git\",\n\t\"admin\":   \"https://github.com/go-kratos/kratos-admin.git\",\n}\n\n// CmdNew represents the new command.\nvar CmdNew = &cobra.Command{\n\tUse:   \"new\",\n\tShort: \"Create a service template\",\n\tLong:  \"Create a service project using the repository template. Example: kratos new helloworld\",\n\tRun:   run,\n}\n\nvar (\n\tnomod   bool\n\trepo    string\n\tbranch  string\n\ttimeout = \"60s\"\n)\n\nfunc init() {\n\tCmdNew.Flags().StringVarP(&repo, \"repo\", \"r\", repo, \"custom repo url\")\n\tCmdNew.Flags().StringVarP(&branch, \"branch\", \"b\", branch, \"repo branch\")\n\tCmdNew.Flags().StringVarP(&timeout, \"timeout\", \"t\", timeout, \"time out\")\n\tCmdNew.Flags().BoolVarP(&nomod, \"nomod\", \"\", nomod, \"retain go mod\")\n}\n\nfunc run(_ *cobra.Command, args []string) {\n\twd, err := os.Getwd()\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tt, err := time.ParseDuration(timeout)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tctx, cancel := context.WithTimeout(context.Background(), t)\n\tdefer cancel()\n\tname := \"\"\n\tif len(args) == 0 {\n\t\tprompt := &survey.Input{\n\t\t\tMessage: \"What is project name ?\",\n\t\t\tHelp:    \"Created project name.\",\n\t\t}\n\t\terr = survey.AskOne(prompt, &name)\n\t\tif err != nil || name == \"\" {\n\t\t\treturn\n\t\t}\n\t} else {\n\t\tname = args[0]\n\t}\n\tprojectName, workingDir := processProjectParams(name, wd)\n\tp := &Project{Name: projectName}\n\tdone := make(chan error, 1)\n\tvar repoURL string\n\tif repo != \"\" {\n\t\trepoURL = repo\n\t} else {\n\t\trepoURL, err = selectRepo()\n\t\tif err != nil {\n\t\t\tfmt.Fprintf(os.Stderr, \"\\033[31mERROR: failed to select repo(%s)\\033[m\\n\", err.Error())\n\t\t\treturn\n\t\t}\n\t}\n\tgo func() {\n\t\tif !nomod {\n\t\t\tdone <- p.New(ctx, workingDir, repoURL, branch)\n\t\t\treturn\n\t\t}\n\t\tprojectRoot := getgomodProjectRoot(workingDir)\n\t\tif gomodIsNotExistIn(projectRoot) {\n\t\t\tdone <- fmt.Errorf(\"🚫 go.mod don't exists in %s\", projectRoot)\n\t\t\treturn\n\t\t}\n\n\t\tpackagePath, e := filepath.Rel(projectRoot, filepath.Join(workingDir, projectName))\n\t\tif e != nil {\n\t\t\tdone <- fmt.Errorf(\"🚫 failed to get relative path: %v\", e)\n\t\t\treturn\n\t\t}\n\t\tpackagePath = strings.ReplaceAll(packagePath, \"\\\\\", \"/\")\n\n\t\tmod, e := base.ModulePath(filepath.Join(projectRoot, \"go.mod\"))\n\t\tif e != nil {\n\t\t\tdone <- fmt.Errorf(\"🚫 failed to parse `go.mod`: %v\", e)\n\t\t\treturn\n\t\t}\n\t\t// Get the relative path for adding a project based on Go modules\n\t\tp.Path = filepath.Join(strings.TrimPrefix(workingDir, projectRoot+\"/\"), p.Name)\n\t\tdone <- p.Add(ctx, workingDir, repoURL, branch, mod, packagePath)\n\t}()\n\tselect {\n\tcase <-ctx.Done():\n\t\tif errors.Is(ctx.Err(), context.DeadlineExceeded) {\n\t\t\tfmt.Fprint(os.Stderr, \"\\033[31mERROR: project creation timed out\\033[m\\n\")\n\t\t\treturn\n\t\t}\n\t\tfmt.Fprintf(os.Stderr, \"\\033[31mERROR: failed to create project(%s)\\033[m\\n\", ctx.Err().Error())\n\tcase err = <-done:\n\t\tif err != nil {\n\t\t\tfmt.Fprintf(os.Stderr, \"\\033[31mERROR: Failed to create project(%s)\\033[m\\n\", err.Error())\n\t\t}\n\t}\n}\n\nfunc processProjectParams(projectName string, workingDir string) (projectNameResult, workingDirResult string) {\n\t_projectDir := projectName\n\t_workingDir := workingDir\n\t// Process ProjectName with system variable\n\tif strings.HasPrefix(projectName, \"~\") {\n\t\thomeDir, err := os.UserHomeDir()\n\t\tif err != nil {\n\t\t\t// cannot get user home return fallback place dir\n\t\t\treturn _projectDir, _workingDir\n\t\t}\n\t\t_projectDir = filepath.Join(homeDir, projectName[2:])\n\t}\n\n\t// check path is relative\n\tif !filepath.IsAbs(projectName) {\n\t\tabsPath, err := filepath.Abs(projectName)\n\t\tif err != nil {\n\t\t\treturn _projectDir, _workingDir\n\t\t}\n\t\t_projectDir = absPath\n\t}\n\n\treturn filepath.Base(_projectDir), filepath.Dir(_projectDir)\n}\n\nfunc getgomodProjectRoot(dir string) string {\n\tif dir == filepath.Dir(dir) {\n\t\treturn dir\n\t}\n\tif gomodIsNotExistIn(dir) {\n\t\treturn getgomodProjectRoot(filepath.Dir(dir))\n\t}\n\treturn dir\n}\n\nfunc gomodIsNotExistIn(dir string) bool {\n\t_, e := os.Stat(filepath.Join(dir, \"go.mod\"))\n\treturn os.IsNotExist(e)\n}\n\nfunc selectRepo() (string, error) {\n\tvar (\n\t\tchoice    string\n\t\tcustomURL string\n\t)\n\tform := huh.NewForm(\n\t\t// 1) Select group (always visible)\n\t\thuh.NewGroup(\n\t\t\thuh.NewSelect[string]().\n\t\t\t\tTitle(\"Select a template\").\n\t\t\t\tOptions(\n\t\t\t\t\thuh.NewOption(\"Service\", \"service\"),\n\t\t\t\t\thuh.NewOption(\"Admin\", \"admin\"),\n\t\t\t\t\thuh.NewOption(\"Custom (enter repo URL)\", \"custom\"),\n\t\t\t\t).\n\t\t\t\tValue(&choice),\n\t\t),\n\t\t// 2) Input group (only visible when choice == \"custom\")\n\t\thuh.NewGroup(\n\t\t\thuh.NewInput().\n\t\t\t\tTitle(\"Enter custom repository URL\").\n\t\t\t\tPlaceholder(\"https://github.com/owner/repo.git\").\n\t\t\t\tValue(&customURL).\n\t\t\t\tValidate(func(s string) error {\n\t\t\t\t\ts = strings.TrimSpace(s)\n\t\t\t\t\tif s == \"\" {\n\t\t\t\t\t\treturn fmt.Errorf(\"repo URL cannot be empty\")\n\t\t\t\t\t}\n\t\t\t\t\treturn nil\n\t\t\t\t}),\n\t\t).WithHideFunc(func() bool {\n\t\t\treturn choice != \"custom\"\n\t\t}),\n\t)\n\tif err := form.Run(); err != nil {\n\t\tpanic(err)\n\t}\n\tif choice == \"custom\" {\n\t\treturn strings.TrimSpace(customURL), nil\n\t}\n\treturn projects[choice], nil\n}\n"
  },
  {
    "path": "cmd/kratos/internal/project/project_linux_test.go",
    "content": "//go:build linux\n\npackage project\n\nimport (\n\t\"testing\"\n)\n\nfunc Test_processProjectParams(t *testing.T) {\n\ttype args struct {\n\t\tprojectName      string\n\t\tfallbackPlaceDir string\n\t}\n\ttests := []struct {\n\t\tname string\n\t\targs args\n\t\twant string\n\t}{\n\t\t{\"absLinux\", args{projectName: \"/home/kratos/awesome/go/demo\", fallbackPlaceDir: \"\"}, \"/home/kratos/awesome/go\"},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tif _, got := processProjectParams(tt.args.projectName, tt.args.fallbackPlaceDir); got != tt.want {\n\t\t\t\tt.Errorf(\"processProjectParams() = %v, want %v\", got, tt.want)\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "cmd/kratos/internal/project/project_test.go",
    "content": "package project\n\nimport (\n\t\"fmt\"\n\t\"go/parser\"\n\t\"go/token\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"testing\"\n\n\t\"github.com/go-kratos/kratos/cmd/kratos/v2/internal/base\"\n)\n\n// TestCmdNew tests the `kratos new` command.\nfunc TestCmdNew(t *testing.T) {\n\tcwd := changeCurrentDir(t)\n\tprojectName := \"helloworld\"\n\n\t// create a new project\n\tCmdNew.SetArgs([]string{projectName})\n\tif err := CmdNew.Execute(); err != nil {\n\t\tt.Fatalf(\"executing command: %v\", err)\n\t}\n\n\t// check that the expected files were created\n\tfor _, file := range []string{\n\t\t\"go.mod\",\n\t\t\"go.sum\",\n\t\t\"README.md\",\n\t\t\"cmd/helloworld/main.go\",\n\t} {\n\t\tif _, err := os.Stat(filepath.Join(cwd, projectName, file)); err != nil {\n\t\t\tt.Errorf(\"expected file %s to exist\", file)\n\t\t}\n\t}\n\n\t// check that the go.mod file contains the expected module name\n\tassertGoMod(t, filepath.Join(cwd, projectName, \"go.mod\"), projectName)\n\n\tassertImportsInclude(t, filepath.Join(cwd, projectName, \"cmd\", projectName, \"wire.go\"), fmt.Sprintf(`\"%s/internal/biz\"`, projectName))\n}\n\n// TestCmdNewNoMod tests the `kratos new` command with the --nomod flag.\nfunc TestCmdNewNoMod(t *testing.T) {\n\tcwd := changeCurrentDir(t)\n\n\t// create a new project\n\tCmdNew.SetArgs([]string{\"project\"})\n\tif err := CmdNew.Execute(); err != nil {\n\t\tt.Fatalf(\"executing command: %v\", err)\n\t}\n\n\t// add new app with --nomod flag\n\tCmdNew.SetArgs([]string{\"--nomod\", \"project/app/user\"})\n\tif err := CmdNew.Execute(); err != nil {\n\t\tt.Fatalf(\"executing command: %v\", err)\n\t}\n\n\t// check that the expected files were created\n\tfor _, file := range []string{\n\t\t\"go.mod\",\n\t\t\"go.sum\",\n\t\t\"README.md\",\n\t\t\"cmd/project/main.go\",\n\t\t\"app/user/cmd/user/main.go\",\n\t} {\n\t\tif _, err := os.Stat(filepath.Join(cwd, \"project\", file)); err != nil {\n\t\t\tt.Errorf(\"expected file %s to exist\", file)\n\t\t}\n\t}\n\n\tassertImportsInclude(t, filepath.Join(cwd, \"project/app/user/cmd/user/wire.go\"), `\"project/app/user/internal/biz\"`)\n}\n\n// assertImportsInclude checks that the file at path contains the expected import.\nfunc assertImportsInclude(t *testing.T, path, expected string) {\n\tt.Helper()\n\n\tgot, err := imports(path)\n\tif err != nil {\n\t\tt.Fatalf(\"getting imports: %v\", err)\n\t}\n\n\tfor _, imp := range got {\n\t\tif imp == expected {\n\t\t\treturn\n\t\t}\n\t}\n\n\tt.Errorf(\"expected imports to include %s, got %v\", expected, got)\n}\n\n// imports returns the imports in the file at path.\nfunc imports(path string) ([]string, error) {\n\tfset := token.NewFileSet()\n\tf, err := parser.ParseFile(fset, path, nil, parser.ImportsOnly)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\timports := make([]string, 0, len(f.Imports))\n\tfor _, s := range f.Imports {\n\t\timports = append(imports, s.Path.Value)\n\t}\n\n\treturn imports, nil\n}\n\n// assertGoMod checks that the go.mod file contains the expected module name.\nfunc assertGoMod(t *testing.T, path, expected string) {\n\tt.Helper()\n\n\tgot, err := base.ModulePath(path)\n\tif err != nil {\n\t\tt.Fatalf(\"getting module path: %v\", err)\n\t}\n\n\tif got != expected {\n\t\tt.Errorf(\"expected module name %s, got %s\", expected, got)\n\t}\n}\n\n// change the working directory to the tempdir\nfunc changeCurrentDir(t *testing.T) string {\n\tt.Helper()\n\n\ttmp := t.TempDir()\n\n\toldCWD, err := os.Getwd()\n\tif err != nil {\n\t\tt.Fatalf(\"getting working directory: %v\", err)\n\t}\n\n\tif err := os.Chdir(tmp); err != nil {\n\t\tt.Fatalf(\"changing working directory: %v\", err)\n\t}\n\tt.Cleanup(func() {\n\t\tif err := os.Chdir(oldCWD); err != nil {\n\t\t\tt.Fatalf(\"restoring working directory: %v\", err)\n\t\t}\n\t})\n\n\treturn tmp\n}\n"
  },
  {
    "path": "cmd/kratos/internal/project/project_windows_test.go",
    "content": "//go:build windows\n\npackage project\n\nimport (\n\t\"testing\"\n)\n\nfunc Test_processProjectParams(t *testing.T) {\n\ttype args struct {\n\t\tprojectName      string\n\t\tfallbackPlaceDir string\n\t}\n\ttests := []struct {\n\t\tname string\n\t\targs args\n\t\twant string\n\t}{\n\t\t{\"absWindows\", args{projectName: \"c:\\\\kratos\\\\awesome\\\\go\\\\demo\", fallbackPlaceDir: \"\"}, \"c:\\\\kratos\\\\awesome\\\\go\"},\n\t\t//{\"relativeWindows\", args{projectName: \"/home/kratos/awesome/go/demo\", fallbackPlaceDir: \"\"}, \"/home/kratos/awesome/go\"},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tif _, got := processProjectParams(tt.args.projectName, tt.args.fallbackPlaceDir); got != tt.want {\n\t\t\t\tt.Errorf(\"getProjectPlaceDir() = %v, want %v\", got, tt.want)\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "cmd/kratos/internal/proto/add/add.go",
    "content": "package add\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"strings\"\n\n\t\"github.com/spf13/cobra\"\n\t\"golang.org/x/mod/modfile\"\n\t\"golang.org/x/text/cases\"\n\t\"golang.org/x/text/language\"\n)\n\n// CmdAdd represents the add command.\nvar CmdAdd = &cobra.Command{\n\tUse:   \"add\",\n\tShort: \"Add a proto API template\",\n\tLong:  \"Add a proto API template. Example: kratos proto add helloworld/v1/hello.proto\",\n\tRun:   run,\n}\n\nfunc run(_ *cobra.Command, args []string) {\n\tif len(args) == 0 {\n\t\tfmt.Println(\"Please enter the proto file or directory\")\n\t\treturn\n\t}\n\tinput := args[0]\n\tn := strings.LastIndex(input, \"/\")\n\tif n == -1 {\n\t\tfmt.Println(\"The proto path needs to be hierarchical.\")\n\t\treturn\n\t}\n\tpath := input[:n]\n\tfileName := input[n+1:]\n\tpkgName := strings.ReplaceAll(path, \"/\", \".\")\n\n\tp := &Proto{\n\t\tName:        fileName,\n\t\tPath:        path,\n\t\tPackage:     pkgName,\n\t\tGoPackage:   goPackage(path),\n\t\tJavaPackage: javaPackage(pkgName),\n\t\tService:     serviceName(fileName),\n\t}\n\tif err := p.Generate(); err != nil {\n\t\tfmt.Println(err)\n\t\treturn\n\t}\n}\n\nfunc modName() string {\n\tmodBytes, err := os.ReadFile(\"go.mod\")\n\tif err != nil {\n\t\tif modBytes, err = os.ReadFile(\"../go.mod\"); err != nil {\n\t\t\treturn \"\"\n\t\t}\n\t}\n\treturn modfile.ModulePath(modBytes)\n}\n\nfunc goPackage(path string) string {\n\ts := strings.Split(path, \"/\")\n\treturn modName() + \"/\" + path + \";\" + s[len(s)-1]\n}\n\nfunc javaPackage(name string) string {\n\treturn name\n}\n\nfunc serviceName(name string) string {\n\treturn toUpperCamelCase(strings.Split(name, \".\")[0])\n}\n\nfunc toUpperCamelCase(s string) string {\n\ts = strings.ReplaceAll(s, \"_\", \" \")\n\ts = cases.Title(language.Und, cases.NoLower).String(s)\n\treturn strings.ReplaceAll(s, \" \", \"\")\n}\n"
  },
  {
    "path": "cmd/kratos/internal/proto/add/add_test.go",
    "content": "package add\n\nimport \"testing\"\n\nfunc TestUnderscoreToUpperCamelCase(t *testing.T) {\n\ttests := []struct {\n\t\tname string\n\t\twant string\n\t}{\n\t\t{\n\t\t\tname: \"hello_world\",\n\t\t\twant: \"HelloWorld\",\n\t\t},\n\t\t{\n\t\t\tname: \"v2_kratos_dev\",\n\t\t\twant: \"V2KratosDev\",\n\t\t},\n\t\t{\n\t\t\tname: \"www_Google_com\",\n\t\t\twant: \"WwwGoogleCom\",\n\t\t},\n\t\t{\n\t\t\tname: \"wwwBaidu_com\",\n\t\t\twant: \"WwwBaiduCom\",\n\t\t},\n\t\t{\n\t\t\tname: \"HelloWorld\",\n\t\t\twant: \"HelloWorld\",\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tif got := toUpperCamelCase(tt.name); got != tt.want {\n\t\t\t\tt.Errorf(\"toUpperCamelCase() = %v, want %v\", got, tt.want)\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "cmd/kratos/internal/proto/add/proto.go",
    "content": "package add\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"path/filepath\"\n)\n\n// Proto is a proto generator.\ntype Proto struct {\n\tName        string\n\tPath        string\n\tService     string\n\tPackage     string\n\tGoPackage   string\n\tJavaPackage string\n}\n\n// Generate generate a proto template.\nfunc (p *Proto) Generate() error {\n\tbody, err := p.execute()\n\tif err != nil {\n\t\treturn err\n\t}\n\twd, err := os.Getwd()\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tto := filepath.Join(wd, p.Path)\n\tif _, err := os.Stat(to); os.IsNotExist(err) {\n\t\tif err := os.MkdirAll(to, 0o700); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tname := filepath.Join(to, p.Name)\n\tif _, err := os.Stat(name); !os.IsNotExist(err) {\n\t\treturn fmt.Errorf(\"%s already exists\", p.Name)\n\t}\n\treturn os.WriteFile(name, body, 0o644)\n}\n"
  },
  {
    "path": "cmd/kratos/internal/proto/add/template.go",
    "content": "package add\n\nimport (\n\t\"bytes\"\n\t\"strings\"\n\t\"text/template\"\n)\n\nconst protoTemplate = `\nsyntax = \"proto3\";\n\npackage {{.Package}};\n\noption go_package = \"{{.GoPackage}}\";\noption java_multiple_files = true;\noption java_package = \"{{.JavaPackage}}\";\n\nservice {{.Service}} {\n\trpc Create{{.Service}} (Create{{.Service}}Request) returns (Create{{.Service}}Reply);\n\trpc Update{{.Service}} (Update{{.Service}}Request) returns (Update{{.Service}}Reply);\n\trpc Delete{{.Service}} (Delete{{.Service}}Request) returns (Delete{{.Service}}Reply);\n\trpc Get{{.Service}} (Get{{.Service}}Request) returns (Get{{.Service}}Reply);\n\trpc List{{.Service}} (List{{.Service}}Request) returns (List{{.Service}}Reply);\n}\n\nmessage Create{{.Service}}Request {}\nmessage Create{{.Service}}Reply {}\n\nmessage Update{{.Service}}Request {}\nmessage Update{{.Service}}Reply {}\n\nmessage Delete{{.Service}}Request {}\nmessage Delete{{.Service}}Reply {}\n\nmessage Get{{.Service}}Request {}\nmessage Get{{.Service}}Reply {}\n\nmessage List{{.Service}}Request {}\nmessage List{{.Service}}Reply {}\n`\n\nfunc (p *Proto) execute() ([]byte, error) {\n\tbuf := new(bytes.Buffer)\n\ttmpl, err := template.New(\"proto\").Parse(strings.TrimSpace(protoTemplate))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif err := tmpl.Execute(buf, p); err != nil {\n\t\treturn nil, err\n\t}\n\treturn buf.Bytes(), nil\n}\n"
  },
  {
    "path": "cmd/kratos/internal/proto/client/client.go",
    "content": "package client\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"os/exec\"\n\t\"path/filepath\"\n\t\"regexp\"\n\t\"strings\"\n\n\t\"github.com/spf13/cobra\"\n\n\t\"github.com/go-kratos/kratos/cmd/kratos/v2/internal/base\"\n)\n\n// CmdClient represents the source command.\nvar CmdClient = &cobra.Command{\n\tUse:   \"client\",\n\tShort: \"Generate the proto client code\",\n\tLong:  \"Generate the proto client code. Example: kratos proto client helloworld.proto\",\n\tRun:   run,\n}\n\nvar protoPath string\n\nfunc init() {\n\tif protoPath = os.Getenv(\"KRATOS_PROTO_PATH\"); protoPath == \"\" {\n\t\tprotoPath = \"./third_party\"\n\t}\n\tCmdClient.Flags().StringVarP(&protoPath, \"proto_path\", \"p\", protoPath, \"proto path\")\n}\n\nfunc run(_ *cobra.Command, args []string) {\n\tif len(args) == 0 {\n\t\tfmt.Println(\"Please enter the proto file or directory\")\n\t\treturn\n\t}\n\tvar (\n\t\terr   error\n\t\tproto = strings.TrimSpace(args[0])\n\t)\n\tif err = look(\"protoc-gen-go\", \"protoc-gen-go-grpc\", \"protoc-gen-go-http\", \"protoc-gen-go-errors\", \"protoc-gen-openapi\"); err != nil {\n\t\t// update the kratos plugins\n\t\tcmd := exec.Command(\"kratos\", \"upgrade\")\n\t\tcmd.Stdout = os.Stdout\n\t\tcmd.Stderr = os.Stderr\n\t\tif err = cmd.Run(); err != nil {\n\t\t\tfmt.Println(err)\n\t\t\treturn\n\t\t}\n\t}\n\tif strings.HasSuffix(proto, \".proto\") {\n\t\terr = generate(proto, args)\n\t} else {\n\t\terr = walk(proto, args)\n\t}\n\tif err != nil {\n\t\tfmt.Println(err)\n\t}\n}\n\nfunc look(name ...string) error {\n\tfor _, n := range name {\n\t\tif _, err := exec.LookPath(n); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc walk(dir string, args []string) error {\n\tif dir == \"\" {\n\t\tdir = \".\"\n\t}\n\treturn filepath.Walk(dir, func(path string, _ os.FileInfo, _ error) error {\n\t\tif ext := filepath.Ext(path); ext != \".proto\" || strings.HasPrefix(path, \"third_party\") {\n\t\t\treturn nil\n\t\t}\n\t\treturn generate(path, args)\n\t})\n}\n\n// generate is used to execute the generate command for the specified proto file\nfunc generate(proto string, args []string) error {\n\tinput := []string{\n\t\t\"--proto_path=.\",\n\t}\n\tif pathExists(protoPath) {\n\t\tinput = append(input, \"--proto_path=\"+protoPath)\n\t}\n\tinputExt := []string{\n\t\t\"--proto_path=\" + base.KratosMod(),\n\t\t\"--proto_path=\" + filepath.Join(base.KratosMod(), \"third_party\"),\n\t\t\"--go_out=paths=source_relative:.\",\n\t\t\"--go-grpc_out=paths=source_relative:.\",\n\t\t\"--go-http_out=paths=source_relative:.\",\n\t\t\"--go-errors_out=paths=source_relative:.\",\n\t\t\"--openapi_out=paths=source_relative:.\",\n\t}\n\tinput = append(input, inputExt...)\n\tprotoBytes, err := os.ReadFile(proto)\n\tif err == nil && len(protoBytes) > 0 {\n\t\tif ok, _ := regexp.Match(`\\n[^/]*(import)\\s+\"validate/validate.proto\"`, protoBytes); ok {\n\t\t\tinput = append(input, \"--validate_out=lang=go,paths=source_relative:.\")\n\t\t}\n\t}\n\tinput = append(input, proto)\n\tfor _, a := range args {\n\t\tif strings.HasPrefix(a, \"-\") {\n\t\t\tinput = append(input, a)\n\t\t}\n\t}\n\tfd := exec.Command(\"protoc\", input...)\n\tfd.Stdout = os.Stdout\n\tfd.Stderr = os.Stderr\n\tfd.Dir = \".\"\n\tif err := fd.Run(); err != nil {\n\t\treturn err\n\t}\n\tfmt.Printf(\"proto: %s\\n\", proto)\n\treturn nil\n}\n\nfunc pathExists(path string) bool {\n\t_, err := os.Stat(path)\n\tif err != nil {\n\t\treturn os.IsExist(err)\n\t}\n\treturn true\n}\n"
  },
  {
    "path": "cmd/kratos/internal/proto/proto.go",
    "content": "package proto\n\nimport (\n\t\"github.com/spf13/cobra\"\n\n\t\"github.com/go-kratos/kratos/cmd/kratos/v2/internal/proto/add\"\n\t\"github.com/go-kratos/kratos/cmd/kratos/v2/internal/proto/client\"\n\t\"github.com/go-kratos/kratos/cmd/kratos/v2/internal/proto/server\"\n)\n\n// CmdProto represents the proto command.\nvar CmdProto = &cobra.Command{\n\tUse:   \"proto\",\n\tShort: \"Generate the proto files\",\n\tLong:  \"Generate the proto files.\",\n}\n\nfunc init() {\n\tCmdProto.AddCommand(add.CmdAdd)\n\tCmdProto.AddCommand(client.CmdClient)\n\tCmdProto.AddCommand(server.CmdServer)\n}\n"
  },
  {
    "path": "cmd/kratos/internal/proto/server/server.go",
    "content": "package server\n\nimport (\n\t\"fmt\"\n\t\"log\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"strings\"\n\n\t\"github.com/emicklei/proto\"\n\t\"github.com/spf13/cobra\"\n\t\"golang.org/x/text/cases\"\n\t\"golang.org/x/text/language\"\n)\n\n// CmdServer the service command.\nvar CmdServer = &cobra.Command{\n\tUse:   \"server\",\n\tShort: \"Generate the proto server implementations\",\n\tLong:  \"Generate the proto server implementations. Example: kratos proto server api/xxx.proto --target-dir=internal/service\",\n\tRun:   run,\n}\nvar targetDir string\n\nfunc init() {\n\tCmdServer.Flags().StringVarP(&targetDir, \"target-dir\", \"t\", \"internal/service\", \"generate target directory\")\n}\n\nfunc run(_ *cobra.Command, args []string) {\n\tif len(args) == 0 {\n\t\tfmt.Fprintln(os.Stderr, \"Please specify the proto file. Example: kratos proto server api/xxx.proto\")\n\t\treturn\n\t}\n\treader, err := os.Open(args[0])\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\tdefer reader.Close()\n\n\tparser := proto.NewParser(reader)\n\tdefinition, err := parser.Parse()\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\n\tvar (\n\t\tpkg string\n\t\tres []*Service\n\t)\n\tproto.Walk(definition,\n\t\tproto.WithOption(func(o *proto.Option) {\n\t\t\tif o.Name == \"go_package\" {\n\t\t\t\tpkg = strings.Split(o.Constant.Source, \";\")[0]\n\t\t\t}\n\t\t}),\n\t\tproto.WithService(func(s *proto.Service) {\n\t\t\tcs := &Service{\n\t\t\t\tPackage: pkg,\n\t\t\t\tService: serviceName(s.Name),\n\t\t\t}\n\t\t\tfor _, e := range s.Elements {\n\t\t\t\tr, ok := e.(*proto.RPC)\n\t\t\t\tif !ok {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tcs.Methods = append(cs.Methods, &Method{\n\t\t\t\t\tService: serviceName(s.Name), Name: rpcName(r.Name), Request: parametersName(r.RequestType),\n\t\t\t\t\tReply: parametersName(r.ReturnsType), Type: getMethodType(r.StreamsRequest, r.StreamsReturns),\n\t\t\t\t})\n\t\t\t}\n\t\t\tres = append(res, cs)\n\t\t}),\n\t)\n\tif _, err := os.Stat(targetDir); os.IsNotExist(err) {\n\t\tfmt.Printf(\"Target directory: %s does not exist\\n\", targetDir)\n\t\treturn\n\t}\n\tfor _, s := range res {\n\t\tto := filepath.Join(targetDir, strings.ToLower(s.Service)+\".go\")\n\t\tif _, err := os.Stat(to); !os.IsNotExist(err) {\n\t\t\tfmt.Fprintf(os.Stderr, \"%s already exists: %s\\n\", s.Service, to)\n\t\t\tcontinue\n\t\t}\n\t\tb, err := s.execute()\n\t\tif err != nil {\n\t\t\tlog.Fatal(err)\n\t\t}\n\t\tif err := os.WriteFile(to, b, 0o644); err != nil {\n\t\t\tlog.Fatal(err)\n\t\t}\n\t\tfmt.Println(to)\n\t}\n}\n\nfunc getMethodType(streamsRequest, streamsReturns bool) MethodType {\n\tif !streamsRequest && !streamsReturns {\n\t\treturn unaryType\n\t} else if streamsRequest && streamsReturns {\n\t\treturn twoWayStreamsType\n\t} else if streamsRequest {\n\t\treturn requestStreamsType\n\t} else if streamsReturns {\n\t\treturn returnsStreamsType\n\t}\n\treturn unaryType\n}\n\nfunc parametersName(name string) string {\n\treturn strings.ReplaceAll(name, \".\", \"_\")\n}\n\nfunc serviceName(name string) string {\n\treturn strings.TrimSuffix(toUpperCamelCase(strings.Split(name, \".\")[0]), \"Service\")\n}\n\nfunc rpcName(name string) string {\n\treturn toUpperCamelCase(strings.Split(name, \".\")[0])\n}\n\nfunc toUpperCamelCase(s string) string {\n\ts = strings.ReplaceAll(s, \"_\", \" \")\n\ts = cases.Title(language.Und, cases.NoLower).String(s)\n\treturn strings.ReplaceAll(s, \" \", \"\")\n}\n"
  },
  {
    "path": "cmd/kratos/internal/proto/server/server_test.go",
    "content": "package server\n\nimport \"testing\"\n\nfunc Test_serviceName(t *testing.T) {\n\ttype args struct {\n\t\tstr string\n\t}\n\ttests := []struct {\n\t\tname string\n\t\targs args\n\t\twant string\n\t}{\n\t\t{\n\t\t\tname: \"serviceName on lowercase words\",\n\t\t\targs: args{str: \"helloworld\"},\n\t\t\twant: \"Helloworld\",\n\t\t},\n\t\t{\n\t\t\tname: \"serviceName on uppercase words\",\n\t\t\targs: args{str: \"HELLOWORLD\"},\n\t\t\twant: \"HELLOWORLD\",\n\t\t},\n\t\t{\n\t\t\tname: \"serviceName on lowercase words with spaces\",\n\t\t\targs: args{str: \"hello world\"},\n\t\t\twant: \"HelloWorld\",\n\t\t},\n\t\t{\n\t\t\tname: \"serviceName on uppercase words with spaces\",\n\t\t\targs: args{str: \"HELLO WORLD\"},\n\t\t\twant: \"HELLOWORLD\",\n\t\t},\n\t\t{\n\t\t\tname: \"serviceName on Lower Camel Case words\",\n\t\t\targs: args{str: \"helloWorld\"},\n\t\t\twant: \"HelloWorld\",\n\t\t},\n\t\t{\n\t\t\tname: \"serviceName on Lower Camel Case words\",\n\t\t\targs: args{str: \"helloWorld\"},\n\t\t\twant: \"HelloWorld\",\n\t\t},\n\t\t{\n\t\t\tname: \"serviceName on Upper Camel Case words\",\n\t\t\targs: args{str: \"HelloWorld\"},\n\t\t\twant: \"HelloWorld\",\n\t\t},\n\t\t{\n\t\t\tname: \"serviceName on Upper Camel Case words\",\n\t\t\targs: args{str: \"hello_world\"},\n\t\t\twant: \"HelloWorld\",\n\t\t},\n\t\t{\n\t\t\tname: \"serviceName with service suffix\",\n\t\t\targs: args{str: \"HelloWorldService\"},\n\t\t\twant: \"HelloWorld\",\n\t\t},\n\t\t{\n\t\t\tname: \"serviceName with space and service suffix\",\n\t\t\targs: args{str: \"Hello world service\"},\n\t\t\twant: \"HelloWorld\",\n\t\t},\n\t\t{\n\t\t\tname: \"serviceName with snake case and service suffix\",\n\t\t\targs: args{str: \"hello_world_service\"},\n\t\t\twant: \"HelloWorld\",\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tif got := serviceName(tt.args.str); got != tt.want {\n\t\t\t\tt.Errorf(\"serviceName() = %v, want %v\", got, tt.want)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc Test_parametersName(t *testing.T) {\n\ttype args struct {\n\t\tname string\n\t}\n\ttests := []struct {\n\t\tname string\n\t\targs args\n\t\twant string\n\t}{\n\t\t{\n\t\t\tname: \"parametersName on not nested\",\n\t\t\targs: args{\n\t\t\t\tname: \"MessageResponse\",\n\t\t\t},\n\t\t\twant: \"MessageResponse\",\n\t\t},\n\t\t{\n\t\t\tname: \"parametersName on One layer of nesting\",\n\t\t\targs: args{\n\t\t\t\tname: \"Message.Response\",\n\t\t\t},\n\t\t\twant: \"Message_Response\",\n\t\t},\n\t\t{\n\t\t\tname: \"parametersName on Two layer of nesting\",\n\t\t\targs: args{\n\t\t\t\tname: \"Message.Message2.Response\",\n\t\t\t},\n\t\t\twant: \"Message_Message2_Response\",\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tif got := parametersName(tt.args.name); got != tt.want {\n\t\t\t\tt.Errorf(\"parametersName() = %v, want %v\", got, tt.want)\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "cmd/kratos/internal/proto/server/template.go",
    "content": "package server\n\nimport (\n\t\"bytes\"\n\t\"html/template\"\n)\n\n//nolint:lll\nvar serviceTemplate = `\n{{- /* delete empty line */ -}}\npackage service\n\nimport (\n\t{{- if .UseContext }}\n\t\"context\"\n\t{{- end }}\n\t{{- if .UseIO }}\n\t\"io\"\n\t{{- end }}\n\n\tpb \"{{ .Package }}\"\n\t{{- if .GoogleEmpty }}\n\t\"google.golang.org/protobuf/types/known/emptypb\"\n\t{{- end }}\n)\n\ntype {{ .Service }}Service struct {\n\tpb.Unimplemented{{ .Service }}Server\n}\n\nfunc New{{ .Service }}Service() *{{ .Service }}Service {\n\treturn &{{ .Service }}Service{}\n}\n\n{{- $s1 := \"google.protobuf.Empty\" }}\n{{- $s2 := \"google_protobuf_Empty\" }}\n{{ range .Methods }}\n{{- if eq .Type 1 }}\nfunc (s *{{ .Service }}Service) {{ .Name }}(ctx context.Context, req {{ if or (eq .Request $s1) (eq .Request $s2) }}*emptypb.Empty{{ else }}*pb.{{ .Request }}{{ end }}) ({{ if or (eq .Reply $s1) (eq .Reply $s2) }}*emptypb.Empty{{ else }}*pb.{{ .Reply }}{{ end }}, error) {\n    return {{ if or (eq .Reply $s1) (eq .Reply $s2) }}&emptypb.Empty{}{{ else }}&pb.{{ .Reply }}{}{{ end }}, nil\n}\n\n{{- else if eq .Type 2 }}\nfunc (s *{{ .Service }}Service) {{ .Name }}(conn pb.{{ .Service }}_{{ .Name }}Server) error {\n\tfor {\n\t\treq, err := conn.Recv()\n\t\tif err == io.EOF {\n\t\t\treturn nil\n\t\t}\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\terr = conn.Send(&pb.{{ .Reply }}{})\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n}\n\n{{- else if eq .Type 3 }}\nfunc (s *{{ .Service }}Service) {{ .Name }}(conn pb.{{ .Service }}_{{ .Name }}Server) error {\n\tfor {\n\t\treq, err := conn.Recv()\n\t\tif err == io.EOF {\n\t\t\treturn conn.SendAndClose(&pb.{{ .Reply }}{})\n\t\t}\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n}\n\n{{- else if eq .Type 4 }}\nfunc (s *{{ .Service }}Service) {{ .Name }}(req {{ if or (eq .Request $s1) (eq .Request $s2) }}*emptypb.Empty{{ else }}*pb.{{ .Request }}{{ end }}, conn pb.{{ .Service }}_{{ .Name }}Server) error {\n    for {\n        err := conn.Send(&pb.{{ .Reply }}{})\n        if err != nil {\n            return err\n        }\n    }\n}\n\n{{- end }}\n{{- end }}\n`\n\ntype MethodType uint8\n\nconst (\n\tunaryType          MethodType = 1\n\ttwoWayStreamsType  MethodType = 2\n\trequestStreamsType MethodType = 3\n\treturnsStreamsType MethodType = 4\n)\n\n// Service is a proto service.\ntype Service struct {\n\tPackage     string\n\tService     string\n\tMethods     []*Method\n\tGoogleEmpty bool\n\n\tUseIO      bool\n\tUseContext bool\n}\n\n// Method is a proto method.\ntype Method struct {\n\tService string\n\tName    string\n\tRequest string\n\tReply   string\n\n\t// type: unary or stream\n\tType MethodType\n}\n\nfunc (s *Service) execute() ([]byte, error) {\n\tconst empty = \"google.protobuf.Empty\"\n\t// another empty style\n\tconst emptyV2 = \"google_protobuf_Empty\"\n\tbuf := new(bytes.Buffer)\n\tfor _, method := range s.Methods {\n\t\tisReqEmpty := method.Request == empty || method.Request == emptyV2\n\t\tisReplyEmpty := method.Reply == empty || method.Reply == emptyV2\n\n\t\tif (method.Type == unaryType && (isReqEmpty || isReplyEmpty)) ||\n\t\t\t(method.Type == returnsStreamsType && isReqEmpty) {\n\t\t\ts.GoogleEmpty = true\n\t\t}\n\t\tif method.Type == twoWayStreamsType || method.Type == requestStreamsType {\n\t\t\ts.UseIO = true\n\t\t}\n\t\tif method.Type == unaryType {\n\t\t\ts.UseContext = true\n\t\t}\n\t}\n\ttmpl, err := template.New(\"service\").Parse(serviceTemplate)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif err := tmpl.Execute(buf, s); err != nil {\n\t\treturn nil, err\n\t}\n\treturn buf.Bytes(), nil\n}\n"
  },
  {
    "path": "cmd/kratos/internal/run/run.go",
    "content": "package run\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"os/exec\"\n\t\"path/filepath\"\n\t\"strings\"\n\n\t\"github.com/AlecAivazis/survey/v2\"\n\t\"github.com/spf13/cobra\"\n)\n\n// CmdRun run project command.\nvar CmdRun = &cobra.Command{\n\tUse:   \"run\",\n\tShort: \"Run project\",\n\tLong:  \"Run project. Example: kratos run\",\n\tRun:   Run,\n}\nvar targetDir string\n\nfunc init() {\n\tCmdRun.Flags().StringVarP(&targetDir, \"work\", \"w\", \"\", \"target working directory\")\n}\n\n// Run run project.\nfunc Run(cmd *cobra.Command, args []string) {\n\tvar dir string\n\tcmdArgs, programArgs := splitArgs(cmd, args)\n\tif len(cmdArgs) > 0 {\n\t\tdir = cmdArgs[0]\n\t}\n\tbase, err := os.Getwd()\n\tif err != nil {\n\t\tfmt.Fprintf(os.Stderr, \"\\033[31mERROR: %s\\033[m\\n\", err)\n\t\treturn\n\t}\n\tif dir == \"\" {\n\t\t// find the directory containing the cmd/*\n\t\tcmdPath, err := findCMD(base)\n\t\tif err != nil {\n\t\t\tfmt.Fprintf(os.Stderr, \"\\033[31mERROR: %s\\033[m\\n\", err)\n\t\t\treturn\n\t\t}\n\t\tswitch len(cmdPath) {\n\t\tcase 0:\n\t\t\tfmt.Fprintf(os.Stderr, \"\\033[31mERROR: %s\\033[m\\n\", \"The cmd directory cannot be found in the current directory\")\n\t\t\treturn\n\t\tcase 1:\n\t\t\tfor _, v := range cmdPath {\n\t\t\t\tdir = v\n\t\t\t}\n\t\tdefault:\n\t\t\tvar cmdPaths []string\n\t\t\tfor k := range cmdPath {\n\t\t\t\tcmdPaths = append(cmdPaths, k)\n\t\t\t}\n\t\t\tprompt := &survey.Select{\n\t\t\t\tMessage:  \"Which directory do you want to run?\",\n\t\t\t\tOptions:  cmdPaths,\n\t\t\t\tPageSize: 10,\n\t\t\t}\n\t\t\te := survey.AskOne(prompt, &dir)\n\t\t\tif e != nil || dir == \"\" {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tdir = cmdPath[dir]\n\t\t}\n\t}\n\tfd := exec.Command(\"go\", append([]string{\"run\", dir}, programArgs...)...)\n\tfd.Stdout = os.Stdout\n\tfd.Stderr = os.Stderr\n\tfd.Dir = dir\n\tchangeWorkingDirectory(fd, targetDir)\n\tif err := fd.Run(); err != nil {\n\t\tfmt.Fprintf(os.Stderr, \"\\033[31mERROR: %s\\033[m\\n\", err.Error())\n\t\treturn\n\t}\n}\n\nfunc splitArgs(cmd *cobra.Command, args []string) (cmdArgs, programArgs []string) {\n\tdashAt := cmd.ArgsLenAtDash()\n\tif dashAt >= 0 {\n\t\treturn args[:dashAt], args[dashAt:]\n\t}\n\treturn args, []string{}\n}\n\nfunc findCMD(base string) (map[string]string, error) {\n\twd, err := os.Getwd()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif !strings.HasSuffix(wd, \"/\") {\n\t\twd += \"/\"\n\t}\n\tvar root bool\n\tnext := func(dir string) (map[string]string, error) {\n\t\tcmdPath := make(map[string]string)\n\t\terr := filepath.Walk(dir, func(walkPath string, info os.FileInfo, _ error) error {\n\t\t\t// multi level directory is not allowed under the cmdPath directory, so it is judged that the path ends with cmdPath.\n\t\t\tif strings.HasSuffix(walkPath, \"cmd\") {\n\t\t\t\tpaths, err := os.ReadDir(walkPath)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\tfor _, fileInfo := range paths {\n\t\t\t\t\tif fileInfo.IsDir() {\n\t\t\t\t\t\tabs := filepath.Join(walkPath, fileInfo.Name())\n\t\t\t\t\t\tcmdPath[strings.TrimPrefix(abs, wd)] = abs\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn nil\n\t\t\t}\n\t\t\tif info.Name() == \"go.mod\" {\n\t\t\t\troot = true\n\t\t\t}\n\t\t\treturn nil\n\t\t})\n\t\treturn cmdPath, err\n\t}\n\tfor i := 0; i < 5; i++ {\n\t\ttmp := base\n\t\tcmd, err := next(tmp)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tif len(cmd) > 0 {\n\t\t\treturn cmd, nil\n\t\t}\n\t\tif root {\n\t\t\tbreak\n\t\t}\n\t\tbase = filepath.Join(base, \"..\")\n\t}\n\treturn map[string]string{\"\": base}, nil\n}\n\nfunc changeWorkingDirectory(cmd *exec.Cmd, targetDir string) {\n\ttargetDir = strings.TrimSpace(targetDir)\n\tif targetDir != \"\" {\n\t\tcmd.Dir = targetDir\n\t}\n}\n"
  },
  {
    "path": "cmd/kratos/internal/upgrade/upgrade.go",
    "content": "package upgrade\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/spf13/cobra\"\n\n\t\"github.com/go-kratos/kratos/cmd/kratos/v2/internal/base\"\n)\n\n// CmdUpgrade represents the upgrade command.\nvar CmdUpgrade = &cobra.Command{\n\tUse:   \"upgrade\",\n\tShort: \"Upgrade the kratos tools\",\n\tLong:  \"Upgrade the kratos tools. Example: kratos upgrade\",\n\tRun:   Run,\n}\n\n// Run upgrade the kratos tools.\nfunc Run(_ *cobra.Command, _ []string) {\n\terr := base.GoInstall(\n\t\t\"github.com/go-kratos/kratos/cmd/kratos/v2@latest\",\n\t\t\"github.com/go-kratos/kratos/cmd/protoc-gen-go-http/v2@latest\",\n\t\t\"github.com/go-kratos/kratos/cmd/protoc-gen-go-errors/v2@latest\",\n\t\t\"google.golang.org/protobuf/cmd/protoc-gen-go@latest\",\n\t\t\"google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest\",\n\t\t\"github.com/google/gnostic/cmd/protoc-gen-openapi@latest\",\n\t)\n\tif err != nil {\n\t\tfmt.Println(err)\n\t}\n}\n"
  },
  {
    "path": "cmd/kratos/main.go",
    "content": "package main\n\nimport (\n\t\"log\"\n\n\t\"github.com/spf13/cobra\"\n\n\t\"github.com/go-kratos/kratos/cmd/kratos/v2/internal/change\"\n\t\"github.com/go-kratos/kratos/cmd/kratos/v2/internal/project\"\n\t\"github.com/go-kratos/kratos/cmd/kratos/v2/internal/proto\"\n\t\"github.com/go-kratos/kratos/cmd/kratos/v2/internal/run\"\n\t\"github.com/go-kratos/kratos/cmd/kratos/v2/internal/upgrade\"\n)\n\nvar rootCmd = &cobra.Command{\n\tUse:     \"kratos\",\n\tShort:   \"Kratos: An elegant toolkit for Go microservices.\",\n\tLong:    `Kratos: An elegant toolkit for Go microservices.`,\n\tVersion: release,\n}\n\nfunc init() {\n\trootCmd.AddCommand(project.CmdNew)\n\trootCmd.AddCommand(proto.CmdProto)\n\trootCmd.AddCommand(upgrade.CmdUpgrade)\n\trootCmd.AddCommand(change.CmdChange)\n\trootCmd.AddCommand(run.CmdRun)\n}\n\nfunc main() {\n\tif err := rootCmd.Execute(); err != nil {\n\t\tlog.Fatal(err)\n\t}\n}\n"
  },
  {
    "path": "cmd/kratos/version.go",
    "content": "package main\n\n// release is the current kratos tool version.\nconst release = \"v2.9.2\"\n"
  },
  {
    "path": "cmd/protoc-gen-go-errors/buf.gen.yaml",
    "content": "version: v2\nplugins:\n  - remote: buf.build/protocolbuffers/go:v1.33.0\n    out: .\n    opt:\n      - paths=source_relative\n"
  },
  {
    "path": "cmd/protoc-gen-go-errors/buf.yaml",
    "content": "version: v2\nname: buf.build/go-kratos/protoc-gen-go-errors\nlint:\n  use:\n    - DEFAULT\n  except:\n    - PACKAGE_DIRECTORY_MATCH\n    - ENUM_VALUE_UPPER_SNAKE_CASE\n    - ENUM_VALUE_PREFIX\n    - ENUM_ZERO_VALUE_SUFFIX\n    - FIELD_LOWER_SNAKE_CASE\ndeps:\n  - buf.build/googleapis/googleapis\nbreaking:\n  use:\n    - FILE\n"
  },
  {
    "path": "cmd/protoc-gen-go-errors/errors/errors.pb.go",
    "content": "// Code generated by protoc-gen-go. DO NOT EDIT.\n// versions:\n// \tprotoc-gen-go v1.26.0\n// \tprotoc        v3.15.7\n// source: errors.proto\n\npackage errors\n\nimport (\n\tprotoreflect \"google.golang.org/protobuf/reflect/protoreflect\"\n\tprotoimpl \"google.golang.org/protobuf/runtime/protoimpl\"\n\tdescriptorpb \"google.golang.org/protobuf/types/descriptorpb\"\n\treflect \"reflect\"\n\tsync \"sync\"\n)\n\nconst (\n\t// Verify that this generated code is sufficiently up-to-date.\n\t_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)\n\t// Verify that runtime/protoimpl is sufficiently up-to-date.\n\t_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)\n)\n\ntype Error struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\tCode     int32             `protobuf:\"varint,1,opt,name=code,proto3\" json:\"code,omitempty\"`\n\tReason   string            `protobuf:\"bytes,2,opt,name=reason,proto3\" json:\"reason,omitempty\"`\n\tMessage  string            `protobuf:\"bytes,3,opt,name=message,proto3\" json:\"message,omitempty\"`\n\tMetadata map[string]string `protobuf:\"bytes,4,rep,name=metadata,proto3\" json:\"metadata,omitempty\" protobuf_key:\"bytes,1,opt,name=key,proto3\" protobuf_val:\"bytes,2,opt,name=value,proto3\"`\n}\n\nfunc (x *Error) Reset() {\n\t*x = Error{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_errors_proto_msgTypes[0]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *Error) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*Error) ProtoMessage() {}\n\nfunc (x *Error) ProtoReflect() protoreflect.Message {\n\tmi := &file_errors_proto_msgTypes[0]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use Error.ProtoReflect.Descriptor instead.\nfunc (*Error) Descriptor() ([]byte, []int) {\n\treturn file_errors_proto_rawDescGZIP(), []int{0}\n}\n\nfunc (x *Error) GetCode() int32 {\n\tif x != nil {\n\t\treturn x.Code\n\t}\n\treturn 0\n}\n\nfunc (x *Error) GetReason() string {\n\tif x != nil {\n\t\treturn x.Reason\n\t}\n\treturn \"\"\n}\n\nfunc (x *Error) GetMessage() string {\n\tif x != nil {\n\t\treturn x.Message\n\t}\n\treturn \"\"\n}\n\nfunc (x *Error) GetMetadata() map[string]string {\n\tif x != nil {\n\t\treturn x.Metadata\n\t}\n\treturn nil\n}\n\nvar file_errors_proto_extTypes = []protoimpl.ExtensionInfo{\n\t{\n\t\tExtendedType:  (*descriptorpb.EnumOptions)(nil),\n\t\tExtensionType: (*int32)(nil),\n\t\tField:         1108,\n\t\tName:          \"errors.default_code\",\n\t\tTag:           \"varint,1108,opt,name=default_code\",\n\t\tFilename:      \"errors.proto\",\n\t},\n\t{\n\t\tExtendedType:  (*descriptorpb.EnumValueOptions)(nil),\n\t\tExtensionType: (*int32)(nil),\n\t\tField:         1109,\n\t\tName:          \"errors.code\",\n\t\tTag:           \"varint,1109,opt,name=code\",\n\t\tFilename:      \"errors.proto\",\n\t},\n}\n\n// Extension fields to descriptorpb.EnumOptions.\nvar (\n\t// optional int32 default_code = 1108;\n\tE_DefaultCode = &file_errors_proto_extTypes[0]\n)\n\n// Extension fields to descriptorpb.EnumValueOptions.\nvar (\n\t// optional int32 code = 1109;\n\tE_Code = &file_errors_proto_extTypes[1]\n)\n\nvar File_errors_proto protoreflect.FileDescriptor\n\nvar file_errors_proto_rawDesc = []byte{\n\t0x0a, 0x0c, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x06,\n\t0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x1a, 0x20, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70,\n\t0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74,\n\t0x6f, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xc3, 0x01, 0x0a, 0x05, 0x45, 0x72, 0x72,\n\t0x6f, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05,\n\t0x52, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e,\n\t0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x12, 0x18,\n\t0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52,\n\t0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x37, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61,\n\t0x64, 0x61, 0x74, 0x61, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x65, 0x72, 0x72,\n\t0x6f, 0x72, 0x73, 0x2e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61,\n\t0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74,\n\t0x61, 0x1a, 0x3b, 0x0a, 0x0d, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x45, 0x6e, 0x74,\n\t0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52,\n\t0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20,\n\t0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x3a, 0x40,\n\t0x0a, 0x0c, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x5f, 0x63, 0x6f, 0x64, 0x65, 0x12, 0x1c,\n\t0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66,\n\t0x2e, 0x45, 0x6e, 0x75, 0x6d, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0xd4, 0x08, 0x20,\n\t0x01, 0x28, 0x05, 0x52, 0x0b, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x43, 0x6f, 0x64, 0x65,\n\t0x3a, 0x36, 0x0a, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x12, 0x21, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c,\n\t0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6e, 0x75, 0x6d, 0x56,\n\t0x61, 0x6c, 0x75, 0x65, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0xd5, 0x08, 0x20, 0x01,\n\t0x28, 0x05, 0x52, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x42, 0x59, 0x0a, 0x18, 0x63, 0x6f, 0x6d, 0x2e,\n\t0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x6b, 0x72, 0x61, 0x74, 0x6f, 0x73, 0x2e, 0x65, 0x72,\n\t0x72, 0x6f, 0x72, 0x73, 0x50, 0x01, 0x5a, 0x2c, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63,\n\t0x6f, 0x6d, 0x2f, 0x67, 0x6f, 0x2d, 0x6b, 0x72, 0x61, 0x74, 0x6f, 0x73, 0x2f, 0x6b, 0x72, 0x61,\n\t0x74, 0x6f, 0x73, 0x2f, 0x76, 0x32, 0x2f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x3b, 0x65, 0x72,\n\t0x72, 0x6f, 0x72, 0x73, 0xa2, 0x02, 0x0c, 0x4b, 0x72, 0x61, 0x74, 0x6f, 0x73, 0x45, 0x72, 0x72,\n\t0x6f, 0x72, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,\n}\n\nvar (\n\tfile_errors_proto_rawDescOnce sync.Once\n\tfile_errors_proto_rawDescData = file_errors_proto_rawDesc\n)\n\nfunc file_errors_proto_rawDescGZIP() []byte {\n\tfile_errors_proto_rawDescOnce.Do(func() {\n\t\tfile_errors_proto_rawDescData = protoimpl.X.CompressGZIP(file_errors_proto_rawDescData)\n\t})\n\treturn file_errors_proto_rawDescData\n}\n\nvar file_errors_proto_msgTypes = make([]protoimpl.MessageInfo, 2)\nvar file_errors_proto_goTypes = []interface{}{\n\t(*Error)(nil),                         // 0: errors.Error\n\tnil,                                   // 1: errors.Error.MetadataEntry\n\t(*descriptorpb.EnumOptions)(nil),      // 2: google.protobuf.EnumOptions\n\t(*descriptorpb.EnumValueOptions)(nil), // 3: google.protobuf.EnumValueOptions\n}\nvar file_errors_proto_depIdxs = []int32{\n\t1, // 0: errors.Error.metadata:type_name -> errors.Error.MetadataEntry\n\t2, // 1: errors.default_code:extendee -> google.protobuf.EnumOptions\n\t3, // 2: errors.code:extendee -> google.protobuf.EnumValueOptions\n\t3, // [3:3] is the sub-list for method output_type\n\t3, // [3:3] is the sub-list for method input_type\n\t3, // [3:3] is the sub-list for extension type_name\n\t1, // [1:3] is the sub-list for extension extendee\n\t0, // [0:1] is the sub-list for field type_name\n}\n\nfunc init() { file_errors_proto_init() }\nfunc file_errors_proto_init() {\n\tif File_errors_proto != nil {\n\t\treturn\n\t}\n\tif !protoimpl.UnsafeEnabled {\n\t\tfile_errors_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*Error); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t}\n\ttype x struct{}\n\tout := protoimpl.TypeBuilder{\n\t\tFile: protoimpl.DescBuilder{\n\t\t\tGoPackagePath: reflect.TypeOf(x{}).PkgPath(),\n\t\t\tRawDescriptor: file_errors_proto_rawDesc,\n\t\t\tNumEnums:      0,\n\t\t\tNumMessages:   2,\n\t\t\tNumExtensions: 2,\n\t\t\tNumServices:   0,\n\t\t},\n\t\tGoTypes:           file_errors_proto_goTypes,\n\t\tDependencyIndexes: file_errors_proto_depIdxs,\n\t\tMessageInfos:      file_errors_proto_msgTypes,\n\t\tExtensionInfos:    file_errors_proto_extTypes,\n\t}.Build()\n\tFile_errors_proto = out.File\n\tfile_errors_proto_rawDesc = nil\n\tfile_errors_proto_goTypes = nil\n\tfile_errors_proto_depIdxs = nil\n}\n"
  },
  {
    "path": "cmd/protoc-gen-go-errors/errors/errors.proto",
    "content": "syntax = \"proto3\";\n\npackage errors;\n\noption go_package = \"github.com/go-kratos/kratos/v2/errors;errors\";\noption java_multiple_files = true;\noption java_package = \"com.github.kratos.errors\";\noption objc_class_prefix = \"KratosErrors\";\n\nimport \"google/protobuf/descriptor.proto\";\n\nmessage Error {\n  int32 code = 1;\n  string reason = 2;\n  string message = 3;\n  map<string, string> metadata = 4;\n};\n\nextend google.protobuf.EnumOptions {\n  int32 default_code = 1108;\n}\n\nextend google.protobuf.EnumValueOptions {\n  int32 code = 1109;\n}\n"
  },
  {
    "path": "cmd/protoc-gen-go-errors/errors.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"strings\"\n\t\"unicode\"\n\n\t\"golang.org/x/text/cases\"\n\t\"golang.org/x/text/language\"\n\n\t\"google.golang.org/protobuf/compiler/protogen\"\n\t\"google.golang.org/protobuf/proto\"\n\n\t\"github.com/go-kratos/kratos/cmd/protoc-gen-go-errors/v2/errors\"\n)\n\nconst (\n\terrorsPackage = protogen.GoImportPath(\"github.com/go-kratos/kratos/v2/errors\")\n\tfmtPackage    = protogen.GoImportPath(\"fmt\")\n)\n\nvar enCases = cases.Title(language.AmericanEnglish, cases.NoLower)\n\n// generateFile generates a _errors.pb.go file containing kratos errors definitions.\nfunc generateFile(gen *protogen.Plugin, file *protogen.File) *protogen.GeneratedFile {\n\tif len(file.Enums) == 0 {\n\t\treturn nil\n\t}\n\tfilename := file.GeneratedFilenamePrefix + \"_errors.pb.go\"\n\tg := gen.NewGeneratedFile(filename, file.GoImportPath)\n\tg.P(\"// Code generated by protoc-gen-go-errors. DO NOT EDIT.\")\n\tg.P()\n\tg.P(\"package \", file.GoPackageName)\n\tg.P()\n\tg.QualifiedGoIdent(fmtPackage.Ident(\"\"))\n\tgenerateFileContent(gen, file, g)\n\treturn g\n}\n\n// generateFileContent generates the kratos errors definitions, excluding the package statement.\nfunc generateFileContent(gen *protogen.Plugin, file *protogen.File, g *protogen.GeneratedFile) {\n\tif len(file.Enums) == 0 {\n\t\treturn\n\t}\n\n\tg.P(\"// This is a compile-time assertion to ensure that this generated file\")\n\tg.P(\"// is compatible with the kratos package it is being compiled against.\")\n\tg.P(\"const _ = \", errorsPackage.Ident(\"SupportPackageIsVersion1\"))\n\tg.P()\n\tindex := 0\n\tfor _, enum := range file.Enums {\n\t\tif !genErrorsReason(gen, file, g, enum) {\n\t\t\tindex++\n\t\t}\n\t}\n\t// If all enums do not contain 'errors.code', the current file is skipped\n\tif index == 0 {\n\t\tg.Skip()\n\t}\n}\n\nfunc genErrorsReason(_ *protogen.Plugin, _ *protogen.File, g *protogen.GeneratedFile, enum *protogen.Enum) bool {\n\tdefaultCode := proto.GetExtension(enum.Desc.Options(), errors.E_DefaultCode)\n\tcode := 0\n\tif ok := defaultCode.(int32); ok != 0 {\n\t\tcode = int(ok)\n\t}\n\tif code > 600 || code < 0 {\n\t\tpanic(fmt.Sprintf(\"Enum '%s' range must be greater than 0 and less than or equal to 600\", string(enum.Desc.Name())))\n\t}\n\tvar ew errorWrapper\n\tfor _, v := range enum.Values {\n\t\tenumCode := code\n\t\teCode := proto.GetExtension(v.Desc.Options(), errors.E_Code)\n\t\tif ok := eCode.(int32); ok != 0 {\n\t\t\tenumCode = int(ok)\n\t\t}\n\t\t// If the current enumeration does not contain 'errors.code'\n\t\t// or the code value exceeds the range, the current enum will be skipped\n\t\tif enumCode > 600 || enumCode < 0 {\n\t\t\tpanic(fmt.Sprintf(\"Enum '%s' range must be greater than 0 and less than or equal to 600\", string(v.Desc.Name())))\n\t\t}\n\t\tif enumCode == 0 {\n\t\t\tcontinue\n\t\t}\n\n\t\tcomment := v.Comments.Leading.String()\n\t\tif comment == \"\" {\n\t\t\tcomment = v.Comments.Trailing.String()\n\t\t}\n\n\t\terr := &errorInfo{\n\t\t\tName:       string(enum.Desc.Name()),\n\t\t\tValue:      string(v.Desc.Name()),\n\t\t\tCamelValue: case2Camel(string(v.Desc.Name())),\n\t\t\tHTTPCode:   enumCode,\n\t\t\tComment:    comment,\n\t\t\tHasComment: len(comment) > 0,\n\t\t}\n\t\tew.Errors = append(ew.Errors, err)\n\t}\n\tif len(ew.Errors) == 0 {\n\t\treturn true\n\t}\n\tg.P(ew.execute())\n\n\treturn false\n}\n\nfunc case2Camel(name string) string {\n\tif !strings.Contains(name, \"_\") {\n\t\tif name == strings.ToUpper(name) {\n\t\t\tname = strings.ToLower(name)\n\t\t}\n\t\treturn enCases.String(name)\n\t}\n\tstrs := strings.Split(name, \"_\")\n\twords := make([]string, 0, len(strs))\n\tfor _, w := range strs {\n\t\thasLower := false\n\t\tfor _, r := range w {\n\t\t\tif unicode.IsLower(r) {\n\t\t\t\thasLower = true\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t\tif !hasLower {\n\t\t\tw = strings.ToLower(w)\n\t\t}\n\t\tw = enCases.String(w)\n\t\twords = append(words, w)\n\t}\n\n\treturn strings.Join(words, \"\")\n}\n"
  },
  {
    "path": "cmd/protoc-gen-go-errors/errorsTemplate.tpl",
    "content": "{{ range .Errors }}\n\n{{ if .HasComment }}{{ .Comment }}{{ end -}}\nfunc Is{{.CamelValue}}(err error) bool {\n\tif err == nil {\n\t\treturn false\n\t}\n\te := errors.FromError(err)\n\treturn e.Reason == {{ .Name }}_{{ .Value }}.String() && e.Code == {{ .HTTPCode }}\n}\n\n{{ if .HasComment }}{{ .Comment }}{{ end -}}\nfunc Error{{ .CamelValue }}(format string, args ...interface{}) *errors.Error {\n\treturn errors.New({{ .HTTPCode }}, {{ .Name }}_{{ .Value }}.String(), fmt.Sprintf(format, args...))\n}\n\n{{- end }}\n"
  },
  {
    "path": "cmd/protoc-gen-go-errors/errors_test.go",
    "content": "package main\n\nimport \"testing\"\n\nfunc Test_case2Camel(t *testing.T) {\n\ttype args struct {\n\t\tname string\n\t}\n\ttests := []struct {\n\t\tname string\n\t\targs args\n\t\twant string\n\t}{\n\t\t{\n\t\t\tname: \"snake1\",\n\t\t\targs: args{\"SYSTEM_ERROR\"},\n\t\t\twant: \"SystemError\",\n\t\t},\n\t\t{\n\t\t\tname: \"snake2\",\n\t\t\targs: args{\"System_Error\"},\n\t\t\twant: \"SystemError\",\n\t\t},\n\t\t{\n\t\t\tname: \"snake3\",\n\t\t\targs: args{\"system_error\"},\n\t\t\twant: \"SystemError\",\n\t\t},\n\t\t{\n\t\t\tname: \"snake4\",\n\t\t\targs: args{\"System_error\"},\n\t\t\twant: \"SystemError\",\n\t\t},\n\t\t{\n\t\t\tname: \"upper1\",\n\t\t\targs: args{\"UNKNOWN\"},\n\t\t\twant: \"Unknown\",\n\t\t},\n\t\t{\n\t\t\tname: \"camel1\",\n\t\t\targs: args{\"SystemError\"},\n\t\t\twant: \"SystemError\",\n\t\t},\n\t\t{\n\t\t\tname: \"camel2\",\n\t\t\targs: args{\"systemError\"},\n\t\t\twant: \"SystemError\",\n\t\t},\n\t\t{\n\t\t\tname: \"lower1\",\n\t\t\targs: args{\"system\"},\n\t\t\twant: \"System\",\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tif got := case2Camel(tt.args.name); got != tt.want {\n\t\t\t\tt.Errorf(\"case2Camel() = %v, want %v\", got, tt.want)\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "cmd/protoc-gen-go-errors/go.mod",
    "content": "module github.com/go-kratos/kratos/cmd/protoc-gen-go-errors/v2\n\ngo 1.22\n\nrequire (\n\tgolang.org/x/text v0.3.8\n\tgoogle.golang.org/protobuf v1.33.0\n)\n"
  },
  {
    "path": "cmd/protoc-gen-go-errors/go.sum",
    "content": "github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=\ngithub.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngolang.org/x/text v0.3.8 h1:nAL+RVCQ9uMn3vJZbV+MRnydTJFPf8qqY42YiA6MrqY=\ngolang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=\ngolang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=\ngolang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngoogle.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=\ngoogle.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=\n"
  },
  {
    "path": "cmd/protoc-gen-go-errors/main.go",
    "content": "package main\n\nimport (\n\t\"flag\"\n\t\"fmt\"\n\n\t\"google.golang.org/protobuf/compiler/protogen\"\n\t\"google.golang.org/protobuf/types/pluginpb\"\n)\n\nvar showVersion = flag.Bool(\"version\", false, \"print the version and exit\")\n\nfunc main() {\n\tflag.Parse()\n\tif *showVersion {\n\t\tfmt.Printf(\"protoc-gen-go-errors %v\\n\", release)\n\t\treturn\n\t}\n\tvar flags flag.FlagSet\n\tprotogen.Options{\n\t\tParamFunc: flags.Set,\n\t}.Run(func(gen *protogen.Plugin) error {\n\t\tgen.SupportedFeatures = uint64(pluginpb.CodeGeneratorResponse_FEATURE_PROTO3_OPTIONAL)\n\t\tfor _, f := range gen.Files {\n\t\t\tif !f.Generate {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tgenerateFile(gen, f)\n\t\t}\n\t\treturn nil\n\t})\n}\n"
  },
  {
    "path": "cmd/protoc-gen-go-errors/template.go",
    "content": "package main\n\nimport (\n\t\"bytes\"\n\t_ \"embed\"\n\t\"text/template\"\n)\n\n//go:embed errorsTemplate.tpl\nvar errorsTemplate string\n\ntype errorInfo struct {\n\tName       string\n\tValue      string\n\tHTTPCode   int\n\tCamelValue string\n\tComment    string\n\tHasComment bool\n}\n\ntype errorWrapper struct {\n\tErrors []*errorInfo\n}\n\nfunc (e *errorWrapper) execute() string {\n\tbuf := new(bytes.Buffer)\n\ttmpl, err := template.New(\"errors\").Parse(errorsTemplate)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tif err := tmpl.Execute(buf, e); err != nil {\n\t\tpanic(err)\n\t}\n\treturn buf.String()\n}\n"
  },
  {
    "path": "cmd/protoc-gen-go-errors/version.go",
    "content": "package main\n\n// release is the current protoc-gen-go-errors version.\nconst release = \"v2.9.2\"\n"
  },
  {
    "path": "cmd/protoc-gen-go-http/go.mod",
    "content": "module github.com/go-kratos/kratos/cmd/protoc-gen-go-http/v2\n\ngo 1.22\n\nrequire (\n\tgoogle.golang.org/genproto v0.0.0-20220519153652-3a47de7e79bd\n\tgoogle.golang.org/protobuf v1.33.0\n)\n"
  },
  {
    "path": "cmd/protoc-gen-go-http/go.sum",
    "content": "cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=\ncloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=\ngithub.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=\ngithub.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=\ngithub.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=\ngithub.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=\ngithub.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=\ngithub.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=\ngithub.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=\ngithub.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI=\ngithub.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=\ngithub.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=\ngithub.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=\ngithub.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=\ngithub.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=\ngithub.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=\ngithub.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=\ngithub.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE=\ngithub.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=\ngithub.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=\ngithub.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=\ngithub.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=\ngithub.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=\ngithub.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=\ngithub.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=\ngithub.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=\ngithub.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=\ngithub.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=\ngithub.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=\ngithub.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=\ngithub.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=\ngithub.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=\ngithub.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=\ngithub.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=\ngithub.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=\ngithub.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=\ngithub.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=\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.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=\ngithub.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=\ngithub.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=\ngithub.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=\ngithub.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=\ngithub.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=\ngithub.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\ngithub.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=\ngithub.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngo.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=\ngolang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=\ngolang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=\ngolang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=\ngolang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=\ngolang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=\ngolang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=\ngolang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=\ngolang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=\ngolang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=\ngolang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=\ngolang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\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/sys v0.0.0-20180830151530-49385e6e1522/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-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/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-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\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.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\ngolang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\ngolang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=\ngolang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=\ngolang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=\ngolang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=\ngolang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngoogle.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=\ngoogle.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=\ngoogle.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=\ngoogle.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=\ngoogle.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=\ngoogle.golang.org/genproto v0.0.0-20220519153652-3a47de7e79bd h1:e0TwkXOdbnH/1x5rc5MZ/VYyiZ4v+RdVfrGMqEwT68I=\ngoogle.golang.org/genproto v0.0.0-20220519153652-3a47de7e79bd/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4=\ngoogle.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=\ngoogle.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=\ngoogle.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=\ngoogle.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=\ngoogle.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=\ngoogle.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=\ngoogle.golang.org/grpc v1.46.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=\ngoogle.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=\ngoogle.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=\ngoogle.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=\ngoogle.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=\ngoogle.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=\ngoogle.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=\ngoogle.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=\ngoogle.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=\ngoogle.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=\ngoogle.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=\ngoogle.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=\ngoogle.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=\ngoogle.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=\ngoogle.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=\ngoogle.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=\ngopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\nhonnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\nhonnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\n"
  },
  {
    "path": "cmd/protoc-gen-go-http/http.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"net/http\"\n\t\"os\"\n\t\"regexp\"\n\t\"strings\"\n\n\t\"google.golang.org/protobuf/reflect/protoreflect\"\n\n\t\"google.golang.org/genproto/googleapis/api/annotations\"\n\t\"google.golang.org/protobuf/compiler/protogen\"\n\t\"google.golang.org/protobuf/proto\"\n\t\"google.golang.org/protobuf/types/descriptorpb\"\n)\n\nconst (\n\tcontextPackage       = protogen.GoImportPath(\"context\")\n\ttransportHTTPPackage = protogen.GoImportPath(\"github.com/go-kratos/kratos/v2/transport/http\")\n\tbindingPackage       = protogen.GoImportPath(\"github.com/go-kratos/kratos/v2/transport/http/binding\")\n)\n\nvar methodSets = make(map[string]int)\n\n// generateFile generates a _http.pb.go file containing kratos errors definitions.\nfunc generateFile(gen *protogen.Plugin, file *protogen.File, omitempty bool, omitemptyPrefix string) *protogen.GeneratedFile {\n\tif len(file.Services) == 0 || (omitempty && !hasHTTPRule(file.Services)) {\n\t\treturn nil\n\t}\n\tfilename := file.GeneratedFilenamePrefix + \"_http.pb.go\"\n\tg := gen.NewGeneratedFile(filename, file.GoImportPath)\n\tg.P(\"// Code generated by protoc-gen-go-http. DO NOT EDIT.\")\n\tg.P(\"// versions:\")\n\tg.P(fmt.Sprintf(\"// - protoc-gen-go-http %s\", release))\n\tg.P(\"// - protoc             \", protocVersion(gen))\n\tif file.Proto.GetOptions().GetDeprecated() {\n\t\tg.P(\"// \", file.Desc.Path(), \" is a deprecated file.\")\n\t} else {\n\t\tg.P(\"// source: \", file.Desc.Path())\n\t}\n\tg.P()\n\tg.P(\"package \", file.GoPackageName)\n\tg.P()\n\tgenerateFileContent(gen, file, g, omitempty, omitemptyPrefix)\n\treturn g\n}\n\n// generateFileContent generates the kratos errors definitions, excluding the package statement.\nfunc generateFileContent(gen *protogen.Plugin, file *protogen.File, g *protogen.GeneratedFile, omitempty bool, omitemptyPrefix string) {\n\tif len(file.Services) == 0 {\n\t\treturn\n\t}\n\tg.P(\"// This is a compile-time assertion to ensure that this generated file\")\n\tg.P(\"// is compatible with the kratos package it is being compiled against.\")\n\tg.P(\"var _ = new(\", contextPackage.Ident(\"Context\"), \")\")\n\tg.P(\"var _ = \", bindingPackage.Ident(\"EncodeURL\"))\n\tg.P(\"const _ = \", transportHTTPPackage.Ident(\"SupportPackageIsVersion1\"))\n\tg.P()\n\n\tfor _, service := range file.Services {\n\t\tgenService(gen, file, g, service, omitempty, omitemptyPrefix)\n\t}\n}\n\nfunc genService(_ *protogen.Plugin, file *protogen.File, g *protogen.GeneratedFile, service *protogen.Service, omitempty bool, omitemptyPrefix string) {\n\tif service.Desc.Options().(*descriptorpb.ServiceOptions).GetDeprecated() {\n\t\tg.P(\"//\")\n\t\tg.P(deprecationComment)\n\t}\n\t// HTTP Server.\n\tsd := &serviceDesc{\n\t\tServiceType: service.GoName,\n\t\tServiceName: string(service.Desc.FullName()),\n\t\tMetadata:    file.Desc.Path(),\n\t}\n\tfor _, method := range service.Methods {\n\t\tif method.Desc.IsStreamingClient() || method.Desc.IsStreamingServer() {\n\t\t\tcontinue\n\t\t}\n\t\trule, ok := proto.GetExtension(method.Desc.Options(), annotations.E_Http).(*annotations.HttpRule)\n\t\tif rule != nil && ok {\n\t\t\tfor _, bind := range rule.AdditionalBindings {\n\t\t\t\tsd.Methods = append(sd.Methods, buildHTTPRule(g, service, method, bind, omitemptyPrefix))\n\t\t\t}\n\t\t\tsd.Methods = append(sd.Methods, buildHTTPRule(g, service, method, rule, omitemptyPrefix))\n\t\t} else if !omitempty {\n\t\t\tpath := fmt.Sprintf(\"%s/%s/%s\", omitemptyPrefix, service.Desc.FullName(), method.Desc.Name())\n\t\t\tsd.Methods = append(sd.Methods, buildMethodDesc(g, method, http.MethodPost, path))\n\t\t}\n\t}\n\tif len(sd.Methods) != 0 {\n\t\tg.P(sd.execute())\n\t}\n}\n\nfunc hasHTTPRule(services []*protogen.Service) bool {\n\tfor _, service := range services {\n\t\tfor _, method := range service.Methods {\n\t\t\tif method.Desc.IsStreamingClient() || method.Desc.IsStreamingServer() {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\trule, ok := proto.GetExtension(method.Desc.Options(), annotations.E_Http).(*annotations.HttpRule)\n\t\t\tif rule != nil && ok {\n\t\t\t\treturn true\n\t\t\t}\n\t\t}\n\t}\n\treturn false\n}\n\nfunc buildHTTPRule(g *protogen.GeneratedFile, service *protogen.Service, m *protogen.Method, rule *annotations.HttpRule, omitemptyPrefix string) *methodDesc {\n\tvar (\n\t\tpath         string\n\t\tmethod       string\n\t\tbody         string\n\t\tresponseBody string\n\t)\n\n\tswitch pattern := rule.Pattern.(type) {\n\tcase *annotations.HttpRule_Get:\n\t\tpath = pattern.Get\n\t\tmethod = http.MethodGet\n\tcase *annotations.HttpRule_Put:\n\t\tpath = pattern.Put\n\t\tmethod = http.MethodPut\n\tcase *annotations.HttpRule_Post:\n\t\tpath = pattern.Post\n\t\tmethod = http.MethodPost\n\tcase *annotations.HttpRule_Delete:\n\t\tpath = pattern.Delete\n\t\tmethod = http.MethodDelete\n\tcase *annotations.HttpRule_Patch:\n\t\tpath = pattern.Patch\n\t\tmethod = http.MethodPatch\n\tcase *annotations.HttpRule_Custom:\n\t\tpath = pattern.Custom.Path\n\t\tmethod = pattern.Custom.Kind\n\t}\n\tif method == \"\" {\n\t\tmethod = http.MethodPost\n\t}\n\tif path == \"\" {\n\t\tpath = fmt.Sprintf(\"%s/%s/%s\", omitemptyPrefix, service.Desc.FullName(), m.Desc.Name())\n\t}\n\tbody = rule.Body\n\tresponseBody = rule.ResponseBody\n\tmd := buildMethodDesc(g, m, method, path)\n\tif method == http.MethodGet || method == http.MethodDelete {\n\t\tif body != \"\" {\n\t\t\t_, _ = fmt.Fprintf(os.Stderr, \"\\u001B[31mWARN\\u001B[m: %s %s body should not be declared.\\n\", method, path)\n\t\t}\n\t} else {\n\t\tif body == \"\" {\n\t\t\t_, _ = fmt.Fprintf(os.Stderr, \"\\u001B[31mWARN\\u001B[m: %s %s does not declare a body.\\n\", method, path)\n\t\t}\n\t}\n\tif body == \"*\" {\n\t\tmd.HasBody = true\n\t\tmd.Body = \"\"\n\t} else if body != \"\" {\n\t\tmd.HasBody = true\n\t\tmd.Body = \".\" + camelCaseVars(body)\n\t} else {\n\t\tmd.HasBody = false\n\t}\n\tif responseBody == \"*\" {\n\t\tmd.ResponseBody = \"\"\n\t} else if responseBody != \"\" {\n\t\tmd.ResponseBody = \".\" + camelCaseVars(responseBody)\n\t}\n\treturn md\n}\n\nfunc buildMethodDesc(g *protogen.GeneratedFile, m *protogen.Method, method, path string) *methodDesc {\n\tdefer func() { methodSets[m.GoName]++ }()\n\n\tvars := buildPathVars(path)\n\n\tfor v, s := range vars {\n\t\tfields := m.Input.Desc.Fields()\n\n\t\tif s != nil {\n\t\t\tpath = replacePath(v, *s, path)\n\t\t}\n\t\tfor _, field := range strings.Split(v, \".\") {\n\t\t\tif strings.TrimSpace(field) == \"\" {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif strings.Contains(field, \":\") {\n\t\t\t\tfield = strings.Split(field, \":\")[0]\n\t\t\t}\n\t\t\tfd := fields.ByName(protoreflect.Name(field))\n\t\t\tif fd == nil {\n\t\t\t\tfmt.Fprintf(os.Stderr, \"\\u001B[31mERROR\\u001B[m: The corresponding field '%s' declaration in message could not be found in '%s'\\n\", v, path)\n\t\t\t\tos.Exit(2)\n\t\t\t}\n\t\t\tif fd.IsMap() {\n\t\t\t\tfmt.Fprintf(os.Stderr, \"\\u001B[31mWARN\\u001B[m: The field in path:'%s' shouldn't be a map.\\n\", v)\n\t\t\t} else if fd.IsList() {\n\t\t\t\tfmt.Fprintf(os.Stderr, \"\\u001B[31mWARN\\u001B[m: The field in path:'%s' shouldn't be a list.\\n\", v)\n\t\t\t} else if fd.Kind() == protoreflect.MessageKind || fd.Kind() == protoreflect.GroupKind {\n\t\t\t\tfields = fd.Message().Fields()\n\t\t\t}\n\t\t}\n\t}\n\tcomment := m.Comments.Leading.String() + m.Comments.Trailing.String()\n\tif comment != \"\" {\n\t\tcomment = \"// \" + m.GoName + strings.TrimPrefix(strings.TrimSuffix(comment, \"\\n\"), \"//\")\n\t}\n\tif m.Desc.Options().(*descriptorpb.MethodOptions).GetDeprecated() {\n\t\tif comment != \"\" {\n\t\t\tcomment += \"\\n\"\n\t\t}\n\t\tcomment += deprecationComment\n\t}\n\treturn &methodDesc{\n\t\tName:         m.GoName,\n\t\tOriginalName: string(m.Desc.Name()),\n\t\tNum:          methodSets[m.GoName],\n\t\tRequest:      g.QualifiedGoIdent(m.Input.GoIdent),\n\t\tReply:        g.QualifiedGoIdent(m.Output.GoIdent),\n\t\tComment:      comment,\n\t\tPath:         path,\n\t\tMethod:       method,\n\t\tHasVars:      len(vars) > 0,\n\t}\n}\n\nfunc buildPathVars(path string) (res map[string]*string) {\n\tif strings.HasSuffix(path, \"/\") {\n\t\tfmt.Fprintf(os.Stderr, \"\\u001B[31mWARN\\u001B[m: Path %s should not end with \\\"/\\\" \\n\", path)\n\t}\n\tpattern := regexp.MustCompile(`(?i){([a-z.0-9_\\s]*)=?([^{}]*)}`)\n\tmatches := pattern.FindAllStringSubmatch(path, -1)\n\tres = make(map[string]*string, len(matches))\n\tfor _, m := range matches {\n\t\tname := strings.TrimSpace(m[1])\n\t\tif len(name) > 1 && len(m[2]) > 0 {\n\t\t\tres[name] = &m[2]\n\t\t} else {\n\t\t\tres[name] = nil\n\t\t}\n\t}\n\treturn\n}\n\nfunc replacePath(name string, value string, path string) string {\n\tpattern := regexp.MustCompile(fmt.Sprintf(`(?i){([\\s]*%s\\b[\\s]*)=?([^{}]*)}`, name))\n\tidx := pattern.FindStringIndex(path)\n\tif len(idx) > 0 {\n\t\tpath = fmt.Sprintf(\"%s{%s:%s}%s\",\n\t\t\tpath[:idx[0]], // The start of the match\n\t\t\tname,\n\t\t\tstrings.ReplaceAll(value, \"*\", \".*\"),\n\t\t\tpath[idx[1]:],\n\t\t)\n\t}\n\treturn path\n}\n\nfunc camelCaseVars(s string) string {\n\tsubs := strings.Split(s, \".\")\n\tvars := make([]string, 0, len(subs))\n\tfor _, sub := range subs {\n\t\tvars = append(vars, camelCase(sub))\n\t}\n\treturn strings.Join(vars, \".\")\n}\n\n// camelCase returns the CamelCased name.\n// If there is an interior underscore followed by a lower case letter,\n// drop the underscore and convert the letter to upper case.\n// There is a remote possibility of this rewrite causing a name collision,\n// but it's so remote we're prepared to pretend it's nonexistent - since the\n// C++ generator lowercase names, it's extremely unlikely to have two fields\n// with different capitalization.\n// In short, _my_field_name_2 becomes XMyFieldName_2.\nfunc camelCase(s string) string {\n\tif s == \"\" {\n\t\treturn \"\"\n\t}\n\tt := make([]byte, 0, 32)\n\ti := 0\n\tif s[0] == '_' {\n\t\t// Need a capital letter; drop the '_'.\n\t\tt = append(t, 'X')\n\t\ti++\n\t}\n\t// Invariant: if the next letter is lower case, it must be converted\n\t// to upper case.\n\t// That is, we process a word at a time, where words are marked by _ or\n\t// upper case letter. Digits are treated as words.\n\tfor ; i < len(s); i++ {\n\t\tc := s[i]\n\t\tif c == '_' && i+1 < len(s) && isASCIILower(s[i+1]) {\n\t\t\tcontinue // Skip the underscore in s.\n\t\t}\n\t\tif isASCIIDigit(c) {\n\t\t\tt = append(t, c)\n\t\t\tcontinue\n\t\t}\n\t\t// Assume we have a letter now - if not, it's a bogus identifier.\n\t\t// The next word is a sequence of characters that must start upper case.\n\t\tif isASCIILower(c) {\n\t\t\tc ^= ' ' // Make it a capital letter.\n\t\t}\n\t\tt = append(t, c) // Guaranteed not lower case.\n\t\t// Accept lower case sequence that follows.\n\t\tfor i+1 < len(s) && isASCIILower(s[i+1]) {\n\t\t\ti++\n\t\t\tt = append(t, s[i])\n\t\t}\n\t}\n\treturn string(t)\n}\n\n// Is c an ASCII lower-case letter?\nfunc isASCIILower(c byte) bool {\n\treturn 'a' <= c && c <= 'z'\n}\n\n// Is c an ASCII digit?\nfunc isASCIIDigit(c byte) bool {\n\treturn '0' <= c && c <= '9'\n}\n\nfunc protocVersion(gen *protogen.Plugin) string {\n\tv := gen.Request.GetCompilerVersion()\n\tif v == nil {\n\t\treturn \"(unknown)\"\n\t}\n\tvar suffix string\n\tif s := v.GetSuffix(); s != \"\" {\n\t\tsuffix = \"-\" + s\n\t}\n\treturn fmt.Sprintf(\"v%d.%d.%d%s\", v.GetMajor(), v.GetMinor(), v.GetPatch(), suffix)\n}\n\nconst deprecationComment = \"// Deprecated: Do not use.\"\n"
  },
  {
    "path": "cmd/protoc-gen-go-http/httpTemplate.tpl",
    "content": "{{$svrType := .ServiceType}}\n{{$svrName := .ServiceName}}\n\n{{- range .MethodSets}}\nconst Operation{{$svrType}}{{.OriginalName}} = \"/{{$svrName}}/{{.OriginalName}}\"\n{{- end}}\n\ntype {{.ServiceType}}HTTPServer interface {\n{{- range .MethodSets}}\n\t{{- if ne .Comment \"\"}}\n\t{{.Comment}}\n\t{{- end}}\n\t{{.Name}}(context.Context, *{{.Request}}) (*{{.Reply}}, error)\n{{- end}}\n}\n\nfunc Register{{.ServiceType}}HTTPServer(s *http.Server, srv {{.ServiceType}}HTTPServer) {\n\tr := s.Route(\"/\")\n\t{{- range .Methods}}\n\tr.{{.Method}}(\"{{.Path}}\", _{{$svrType}}_{{.Name}}{{.Num}}_HTTP_Handler(srv))\n\t{{- end}}\n}\n\n{{range .Methods}}\nfunc _{{$svrType}}_{{.Name}}{{.Num}}_HTTP_Handler(srv {{$svrType}}HTTPServer) func(ctx http.Context) error {\n\treturn func(ctx http.Context) error {\n\t\tvar in {{.Request}}\n\t\t{{- if .HasBody}}\n\t\tif err := ctx.Bind(&in{{.Body}}); err != nil {\n\t\t\treturn err\n\t\t}\n\t\t{{- end}}\n\t\tif err := ctx.BindQuery(&in); err != nil {\n\t\t\treturn err\n\t\t}\n\t\t{{- if .HasVars}}\n\t\tif err := ctx.BindVars(&in); err != nil {\n\t\t\treturn err\n\t\t}\n\t\t{{- end}}\n\t\thttp.SetOperation(ctx,Operation{{$svrType}}{{.OriginalName}})\n\t\th := ctx.Middleware(func(ctx context.Context, req interface{}) (interface{}, error) {\n\t\t\treturn srv.{{.Name}}(ctx, req.(*{{.Request}}))\n\t\t})\n\t\tout, err := h(ctx, &in)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\treply := out.(*{{.Reply}})\n\t\treturn ctx.Result(200, reply{{.ResponseBody}})\n\t}\n}\n{{end}}\n\ntype {{.ServiceType}}HTTPClient interface {\n{{- range .MethodSets}}\n\t{{- if ne .Comment \"\"}}\n\t{{.Comment}}\n\t{{- end}}\n\t{{.Name}}(ctx context.Context, req *{{.Request}}, opts ...http.CallOption) (rsp *{{.Reply}}, err error)\n{{- end}}\n}\n\ntype {{.ServiceType}}HTTPClientImpl struct{\n\tcc *http.Client\n}\n\nfunc New{{.ServiceType}}HTTPClient (client *http.Client) {{.ServiceType}}HTTPClient {\n\treturn &{{.ServiceType}}HTTPClientImpl{client}\n}\n\n{{range .MethodSets}}\n\t{{- if ne .Comment \"\"}}\n\t{{.Comment}}\n\t{{- end}}\nfunc (c *{{$svrType}}HTTPClientImpl) {{.Name}}(ctx context.Context, in *{{.Request}}, opts ...http.CallOption) (*{{.Reply}}, error) {\n\tvar out {{.Reply}}\n\tpattern := \"{{.Path}}\"\n\tpath := binding.EncodeURL(pattern, in, {{not .HasBody}})\n\topts = append(opts, http.Operation(Operation{{$svrType}}{{.OriginalName}}))\n\topts = append(opts, http.PathTemplate(pattern))\n\t{{if .HasBody -}}\n\terr := c.cc.Invoke(ctx, \"{{.Method}}\", path, in{{.Body}}, &out{{.ResponseBody}}, opts...)\n\t{{else -}}\n\terr := c.cc.Invoke(ctx, \"{{.Method}}\", path, nil, &out{{.ResponseBody}}, opts...)\n\t{{end -}}\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn &out, nil\n}\n{{end}}\n"
  },
  {
    "path": "cmd/protoc-gen-go-http/http_test.go",
    "content": "package main\n\nimport (\n\t\"reflect\"\n\t\"testing\"\n)\n\nfunc TestNoParameters(t *testing.T) {\n\tpath := \"/test/noparams\"\n\tm := buildPathVars(path)\n\tif !reflect.DeepEqual(m, map[string]*string{}) {\n\t\tt.Fatalf(\"Map should be empty\")\n\t}\n}\n\nfunc TestSingleParam(t *testing.T) {\n\tpath := \"/test/{message.id}\"\n\tm := buildPathVars(path)\n\tif !reflect.DeepEqual(len(m), 1) {\n\t\tt.Fatalf(\"len(m) not is 1\")\n\t}\n\tif m[\"message.id\"] != nil {\n\t\tt.Fatalf(`m[\"message.id\"] should be empty`)\n\t}\n}\n\nfunc TestTwoParametersReplacement(t *testing.T) {\n\tpath := \"/test/{message.id}/{message.name=messages/*}\"\n\tm := buildPathVars(path)\n\tif len(m) != 2 {\n\t\tt.Fatal(\"len(m) should be 2\")\n\t}\n\tif m[\"message.id\"] != nil {\n\t\tt.Fatal(`m[\"message.id\"] should be nil`)\n\t}\n\tif m[\"message.name\"] == nil {\n\t\tt.Fatal(`m[\"message.name\"] should not be nil`)\n\t}\n\tif *m[\"message.name\"] != \"messages/*\" {\n\t\tt.Fatal(`m[\"message.name\"] should be \"messages/*\"`)\n\t}\n}\n\nfunc TestNoReplacePath(t *testing.T) {\n\tpath := \"/test/{message.id=test}\"\n\tif !reflect.DeepEqual(replacePath(\"message.id\", \"test\", path), \"/test/{message.id:test}\") {\n\t\tt.Fatal(`replacePath(\"message.id\", \"test\", path) should be \"/test/{message.id:test}\"`)\n\t}\n\tpath = \"/test/{message.id=test/*}\"\n\tif !reflect.DeepEqual(replacePath(\"message.id\", \"test/*\", path), \"/test/{message.id:test/.*}\") {\n\t\tt.Fatal(`replacePath(\"message.id\", \"test/*\", path) should be \"/test/{message.id:test/.*}\"`)\n\t}\n}\n\nfunc TestReplacePath(t *testing.T) {\n\tpath := \"/test/{message.id}/{message.name=messages/*}\"\n\tnewPath := replacePath(\"message.name\", \"messages/*\", path)\n\tif !reflect.DeepEqual(\"/test/{message.id}/{message.name:messages/.*}\", newPath) {\n\t\tt.Fatal(`replacePath(\"message.name\", \"messages/*\", path) should be \"/test/{message.id}/{message.name:messages/.*}\"`)\n\t}\n}\n\nfunc TestIteration(t *testing.T) {\n\tpath := \"/test/{message.id}/{message.name=messages/*}\"\n\tvars := buildPathVars(path)\n\tfor v, s := range vars {\n\t\tif s != nil {\n\t\t\tpath = replacePath(v, *s, path)\n\t\t}\n\t}\n\tif !reflect.DeepEqual(\"/test/{message.id}/{message.name:messages/.*}\", path) {\n\t\tt.Fatal(`replacePath(\"message.name\", \"messages/*\", path) should be \"/test/{message.id}/{message.name:messages/.*}\"`)\n\t}\n}\n\nfunc TestIterationMiddle(t *testing.T) {\n\tpath := \"/test/{message.name=messages/*}/books\"\n\tvars := buildPathVars(path)\n\tfor v, s := range vars {\n\t\tif s != nil {\n\t\t\tpath = replacePath(v, *s, path)\n\t\t}\n\t}\n\tif !reflect.DeepEqual(\"/test/{message.name:messages/.*}/books\", path) {\n\t\tt.Fatal(`replacePath(\"message.name\", \"messages/*\", path) should be \"/test/{message.name:messages/.*}/books\"`)\n\t}\n}\n\nfunc TestReplaceBoundary(t *testing.T) {\n\tpath := \"/test/{message.namespace=*}/name/{message.name=*}\"\n\tvars := buildPathVars(path)\n\tfor v, s := range vars {\n\t\tif s != nil {\n\t\t\tpath = replacePath(v, *s, path)\n\t\t}\n\t}\n\tif !reflect.DeepEqual(\"/test/{message.namespace:.*}/name/{message.name:.*}\", path) {\n\t\tt.Fatal(`\"/test/{message.namespace=*}/name/{message.name=*}\" should be \"/test/{message.namespace:.*}/name/{message.name:.*}\"`)\n\t}\n}\n"
  },
  {
    "path": "cmd/protoc-gen-go-http/main.go",
    "content": "package main\n\nimport (\n\t\"flag\"\n\t\"fmt\"\n\n\t\"google.golang.org/protobuf/compiler/protogen\"\n\t\"google.golang.org/protobuf/types/pluginpb\"\n)\n\nvar (\n\tshowVersion     = flag.Bool(\"version\", false, \"print the version and exit\")\n\tomitempty       = flag.Bool(\"omitempty\", true, \"omit if google.api is empty\")\n\tomitemptyPrefix = flag.String(\"omitempty_prefix\", \"\", \"omit if google.api is empty\")\n)\n\nfunc main() {\n\tflag.Parse()\n\tif *showVersion {\n\t\tfmt.Printf(\"protoc-gen-go-http %v\\n\", release)\n\t\treturn\n\t}\n\tprotogen.Options{\n\t\tParamFunc: flag.CommandLine.Set,\n\t}.Run(func(gen *protogen.Plugin) error {\n\t\tgen.SupportedFeatures = uint64(pluginpb.CodeGeneratorResponse_FEATURE_PROTO3_OPTIONAL)\n\t\tfor _, f := range gen.Files {\n\t\t\tif !f.Generate {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tgenerateFile(gen, f, *omitempty, *omitemptyPrefix)\n\t\t}\n\t\treturn nil\n\t})\n}\n"
  },
  {
    "path": "cmd/protoc-gen-go-http/template.go",
    "content": "package main\n\nimport (\n\t\"bytes\"\n\t_ \"embed\"\n\t\"strings\"\n\t\"text/template\"\n)\n\n//go:embed httpTemplate.tpl\nvar httpTemplate string\n\ntype serviceDesc struct {\n\tServiceType string // Greeter\n\tServiceName string // helloworld.Greeter\n\tMetadata    string // api/helloworld/helloworld.proto\n\tMethods     []*methodDesc\n\tMethodSets  map[string]*methodDesc\n}\n\ntype methodDesc struct {\n\t// method\n\tName         string\n\tOriginalName string // The parsed original name\n\tNum          int\n\tRequest      string\n\tReply        string\n\tComment      string\n\t// http_rule\n\tPath         string\n\tMethod       string\n\tHasVars      bool\n\tHasBody      bool\n\tBody         string\n\tResponseBody string\n}\n\nfunc (s *serviceDesc) execute() string {\n\ts.MethodSets = make(map[string]*methodDesc)\n\tfor _, m := range s.Methods {\n\t\ts.MethodSets[m.Name] = m\n\t}\n\tbuf := new(bytes.Buffer)\n\ttmpl, err := template.New(\"http\").Parse(strings.TrimSpace(httpTemplate))\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tif err := tmpl.Execute(buf, s); err != nil {\n\t\tpanic(err)\n\t}\n\treturn strings.Trim(buf.String(), \"\\r\\n\")\n}\n"
  },
  {
    "path": "cmd/protoc-gen-go-http/version.go",
    "content": "package main\n\n// release is the current protoc-gen-go-http version.\nconst release = \"v2.9.2\"\n"
  },
  {
    "path": "codecov.yml",
    "content": "ignore:\n  - \"examples\"\n  - \"**/*.pb.go\"\n"
  },
  {
    "path": "config/README.md",
    "content": "# Config\n\n## kubernetes\n\n```shell\ngo get -u github.com/go-kratos/kratos/contrib/config/kubernetes/v2\n```\n\n## apollo\n\n```shell\ngo get -u github.com/go-kratos/kratos/contrib/config/apollo/v2\n```\n\n## etcd\n\n```shell\ngo get -u github.com/go-kratos/kratos/contrib/config/etcd/v2\n```\n\n## nacos\n\n```shell\ngo get -u github.com/go-kratos/kratos/contrib/config/nacos/v2\n```\n"
  },
  {
    "path": "config/config.go",
    "content": "package config\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"reflect\"\n\t\"sync\"\n\t\"time\"\n\n\t\"dario.cat/mergo\"\n\n\t// init encoding\n\t_ \"github.com/go-kratos/kratos/v2/encoding/json\"\n\t_ \"github.com/go-kratos/kratos/v2/encoding/proto\"\n\t_ \"github.com/go-kratos/kratos/v2/encoding/xml\"\n\t_ \"github.com/go-kratos/kratos/v2/encoding/yaml\"\n\t\"github.com/go-kratos/kratos/v2/log\"\n)\n\nvar _ Config = (*config)(nil)\n\nvar ErrNotFound = errors.New(\"key not found\") // ErrNotFound is key not found.\n\n// Observer is config observer.\ntype Observer func(string, Value)\n\n// Config is a config interface.\ntype Config interface {\n\tLoad() error\n\tScan(v any) error\n\tValue(key string) Value\n\tWatch(key string, o Observer) error\n\tClose() error\n}\n\ntype config struct {\n\topts      options\n\treader    Reader\n\tcached    sync.Map\n\tobservers sync.Map\n\twatchers  []Watcher\n}\n\n// New a config with options.\nfunc New(opts ...Option) Config {\n\to := options{\n\t\tdecoder:  defaultDecoder,\n\t\tresolver: defaultResolver,\n\t\tmerge: func(dst, src any) error {\n\t\t\treturn mergo.Map(dst, src, mergo.WithOverride)\n\t\t},\n\t}\n\tfor _, opt := range opts {\n\t\topt(&o)\n\t}\n\treturn &config{\n\t\topts:   o,\n\t\treader: newReader(o),\n\t}\n}\n\nfunc (c *config) watch(w Watcher) {\n\tfor {\n\t\tkvs, err := w.Next()\n\t\tif err != nil {\n\t\t\tif errors.Is(err, context.Canceled) {\n\t\t\t\tlog.Infof(\"watcher's ctx cancel : %v\", err)\n\t\t\t\treturn\n\t\t\t}\n\t\t\ttime.Sleep(time.Second)\n\t\t\tlog.Errorf(\"failed to watch next config: %v\", err)\n\t\t\tcontinue\n\t\t}\n\t\tif err := c.reader.Merge(kvs...); err != nil {\n\t\t\tlog.Errorf(\"failed to merge next config: %v\", err)\n\t\t\tcontinue\n\t\t}\n\t\tif err := c.reader.Resolve(); err != nil {\n\t\t\tlog.Errorf(\"failed to resolve next config: %v\", err)\n\t\t\tcontinue\n\t\t}\n\t\tc.cached.Range(func(key, value any) bool {\n\t\t\tk := key.(string)\n\t\t\tv := value.(Value)\n\t\t\tif n, ok := c.reader.Value(k); ok && reflect.TypeOf(n.Load()) == reflect.TypeOf(v.Load()) && !reflect.DeepEqual(n.Load(), v.Load()) {\n\t\t\t\tv.Store(n.Load())\n\t\t\t\tif o, ok := c.observers.Load(k); ok {\n\t\t\t\t\to.(Observer)(k, v)\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn true\n\t\t})\n\t}\n}\n\nfunc (c *config) Load() error {\n\tfor _, src := range c.opts.sources {\n\t\tkvs, err := src.Load()\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tfor _, v := range kvs {\n\t\t\tlog.Debugf(\"config loaded: %s format: %s\", v.Key, v.Format)\n\t\t}\n\t\tif err = c.reader.Merge(kvs...); err != nil {\n\t\t\tlog.Errorf(\"failed to merge config source: %v\", err)\n\t\t\treturn err\n\t\t}\n\t\tw, err := src.Watch()\n\t\tif err != nil {\n\t\t\tlog.Errorf(\"failed to watch config source: %v\", err)\n\t\t\treturn err\n\t\t}\n\t\tc.watchers = append(c.watchers, w)\n\t\tgo c.watch(w)\n\t}\n\tif err := c.reader.Resolve(); err != nil {\n\t\tlog.Errorf(\"failed to resolve config source: %v\", err)\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc (c *config) Value(key string) Value {\n\tif v, ok := c.cached.Load(key); ok {\n\t\treturn v.(Value)\n\t}\n\tif v, ok := c.reader.Value(key); ok {\n\t\tc.cached.Store(key, v)\n\t\treturn v\n\t}\n\treturn &errValue{err: ErrNotFound}\n}\n\nfunc (c *config) Scan(v any) error {\n\tdata, err := c.reader.Source()\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn unmarshalJSON(data, v)\n}\n\nfunc (c *config) Watch(key string, o Observer) error {\n\tif v := c.Value(key); v.Load() == nil {\n\t\treturn ErrNotFound\n\t}\n\tc.observers.Store(key, o)\n\treturn nil\n}\n\nfunc (c *config) Close() error {\n\tfor _, w := range c.watchers {\n\t\tif err := w.Stop(); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n\n// Get retrieves a config value by key and scans it into the target type.\nfunc Get[T any](c Config, key string) (T, error) {\n\tvar t T\n\tv := c.Value(key)\n\n\tif v.Load() == nil {\n\t\treturn t, ErrNotFound\n\t}\n\n\tswitch any(t).(type) {\n\tcase bool:\n\t\tb, err := v.Bool()\n\t\treturn any(b).(T), err\n\tcase int64:\n\t\ti, err := v.Int()\n\t\treturn any(i).(T), err\n\tcase int:\n\t\ti, err := v.Int()\n\t\treturn any(int(i)).(T), err\n\tcase float64:\n\t\tf, err := v.Float()\n\t\treturn any(f).(T), err\n\tcase string:\n\t\ts, err := v.String()\n\t\treturn any(s).(T), err\n\t}\n\n\terr := v.Scan(&t)\n\treturn t, err\n}\n"
  },
  {
    "path": "config/config_test.go",
    "content": "package config\n\nimport (\n\t\"errors\"\n\t\"testing\"\n\n\t\"dario.cat/mergo\"\n)\n\nconst (\n\t_testJSON = `\n{\n    \"server\":{\n        \"http\":{\n            \"addr\":\"0.0.0.0\",\n\t\t\t\"port\":80,\n            \"timeout\":0.5,\n\t\t\t\"enable_ssl\":true\n        },\n        \"grpc\":{\n            \"addr\":\"0.0.0.0\",\n\t\t\t\"port\":10080,\n            \"timeout\":0.2\n        }\n    },\n    \"data\":{\n        \"database\":{\n            \"driver\":\"mysql\",\n            \"source\":\"root:root@tcp(127.0.0.1:3306)/karta_id?parseTime=true\"\n        }\n    },\n\t\"endpoints\":[\n\t\t\"www.aaa.com\",\n\t\t\"www.bbb.org\"\n\t]\n}`\n)\n\ntype testConfigStruct struct {\n\tServer struct {\n\t\tHTTP struct {\n\t\t\tAddr      string  `json:\"addr\"`\n\t\t\tPort      int     `json:\"port\"`\n\t\t\tTimeout   float64 `json:\"timeout\"`\n\t\t\tEnableSSL bool    `json:\"enable_ssl\"`\n\t\t} `json:\"http\"`\n\t\tGRPC struct {\n\t\t\tAddr    string  `json:\"addr\"`\n\t\t\tPort    int     `json:\"port\"`\n\t\t\tTimeout float64 `json:\"timeout\"`\n\t\t} `json:\"grpc\"`\n\t} `json:\"server\"`\n\tData struct {\n\t\tDatabase struct {\n\t\t\tDriver string `json:\"driver\"`\n\t\t\tSource string `json:\"source\"`\n\t\t} `json:\"database\"`\n\t} `json:\"data\"`\n\tEndpoints []string `json:\"endpoints\"`\n}\n\ntype testJSONSource struct {\n\tdata string\n\tsig  chan struct{}\n\terr  chan struct{}\n}\n\nfunc newTestJSONSource(data string) *testJSONSource {\n\treturn &testJSONSource{data: data, sig: make(chan struct{}), err: make(chan struct{})}\n}\n\nfunc (p *testJSONSource) Load() ([]*KeyValue, error) {\n\tkv := &KeyValue{\n\t\tKey:    \"json\",\n\t\tValue:  []byte(p.data),\n\t\tFormat: \"json\",\n\t}\n\treturn []*KeyValue{kv}, nil\n}\n\nfunc (p *testJSONSource) Watch() (Watcher, error) {\n\treturn newTestWatcher(p.sig, p.err), nil\n}\n\ntype testWatcher struct {\n\tsig  chan struct{}\n\terr  chan struct{}\n\texit chan struct{}\n}\n\nfunc newTestWatcher(sig, err chan struct{}) Watcher {\n\treturn &testWatcher{sig: sig, err: err, exit: make(chan struct{})}\n}\n\nfunc (w *testWatcher) Next() ([]*KeyValue, error) {\n\tselect {\n\tcase <-w.sig:\n\t\treturn nil, nil\n\tcase <-w.err:\n\t\treturn nil, errors.New(\"error\")\n\tcase <-w.exit:\n\t\treturn nil, nil\n\t}\n}\n\nfunc (w *testWatcher) Stop() error {\n\tclose(w.exit)\n\treturn nil\n}\n\nfunc TestConfig(t *testing.T) {\n\tvar (\n\t\terr            error\n\t\thttpAddr       = \"0.0.0.0\"\n\t\thttpTimeout    = 0.5\n\t\tgrpcPort       = 10080\n\t\tendpoint1      = \"www.aaa.com\"\n\t\tdatabaseDriver = \"mysql\"\n\t)\n\n\tc := New(\n\t\tWithSource(newTestJSONSource(_testJSON)),\n\t\tWithDecoder(defaultDecoder),\n\t\tWithResolver(defaultResolver),\n\t)\n\terr = c.Close()\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tjSource := newTestJSONSource(_testJSON)\n\topts := options{\n\t\tsources:  []Source{jSource},\n\t\tdecoder:  defaultDecoder,\n\t\tresolver: defaultResolver,\n\t\tmerge: func(dst, src any) error {\n\t\t\treturn mergo.Map(dst, src, mergo.WithOverride)\n\t\t},\n\t}\n\tcf := &config{}\n\tcf.opts = opts\n\tcf.reader = newReader(opts)\n\n\terr = cf.Load()\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tdriver, err := cf.Value(\"data.database.driver\").String()\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif databaseDriver != driver {\n\t\tt.Fatal(\"databaseDriver is not equal to val\")\n\t}\n\n\tdriverGet, err := Get[string](cf, \"data.database.driver\")\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif databaseDriver != driverGet {\n\t\tt.Errorf(\"Get[string] want: %s, got: %s\", databaseDriver, driverGet)\n\t}\n\n\ttype HTTPConfig struct {\n\t\tAddr string `json:\"addr\"`\n\t\tPort int    `json:\"port\"`\n\t}\n\tv, err := Get[HTTPConfig](cf, \"server.http\")\n\tif err != nil {\n\t\tt.Fatal(err)\n\t} else if v.Addr != httpAddr {\n\t\tt.Errorf(\"Get[HttpConfig] Addr want: %s, got: %s\", httpAddr, v.Addr)\n\t} else if v.Port != 80 {\n\t\tt.Errorf(\"Get[HttpConfig] Port want: 80, got: %d\", v.Port)\n\t}\n\n\terr = cf.Watch(\"endpoints\", func(string, Value) {})\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tjSource.sig <- struct{}{}\n\tjSource.err <- struct{}{}\n\n\tvar testConf testConfigStruct\n\terr = cf.Scan(&testConf)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif httpAddr != testConf.Server.HTTP.Addr {\n\t\tt.Errorf(\"testConf.Server.HTTP.Addr want: %s, got: %s\", httpAddr, testConf.Server.HTTP.Addr)\n\t}\n\tif httpTimeout != testConf.Server.HTTP.Timeout {\n\t\tt.Errorf(\"testConf.Server.HTTP.Timeout want: %.1f, got: %.1f\", httpTimeout, testConf.Server.HTTP.Timeout)\n\t}\n\tif !testConf.Server.HTTP.EnableSSL {\n\t\tt.Error(\"testConf.Server.HTTP.EnableSSL is not equal to true\")\n\t}\n\tif grpcPort != testConf.Server.GRPC.Port {\n\t\tt.Errorf(\"testConf.Server.GRPC.Port want: %d, got: %d\", grpcPort, testConf.Server.GRPC.Port)\n\t}\n\tif endpoint1 != testConf.Endpoints[0] {\n\t\tt.Errorf(\"testConf.Endpoints[0] want: %s, got: %s\", endpoint1, testConf.Endpoints[0])\n\t}\n\tif len(testConf.Endpoints) != 2 {\n\t\tt.Error(\"len(testConf.Endpoints) is not equal to 2\")\n\t}\n}\n"
  },
  {
    "path": "config/env/env.go",
    "content": "package env\n\nimport (\n\t\"os\"\n\t\"strings\"\n\n\t\"github.com/go-kratos/kratos/v2/config\"\n)\n\ntype env struct {\n\tprefixes []string\n}\n\nfunc NewSource(prefixes ...string) config.Source {\n\treturn &env{prefixes: prefixes}\n}\n\nfunc (e *env) Load() (kvs []*config.KeyValue, err error) {\n\treturn e.load(os.Environ()), nil\n}\n\nfunc (e *env) load(envs []string) []*config.KeyValue {\n\tvar kvs []*config.KeyValue\n\tfor _, env := range envs {\n\t\tk, v, _ := strings.Cut(env, \"=\")\n\t\tif k == \"\" {\n\t\t\tcontinue\n\t\t}\n\t\tif len(e.prefixes) > 0 {\n\t\t\tprefix, ok := matchPrefix(e.prefixes, k)\n\t\t\tif !ok || k == prefix {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tk = strings.TrimPrefix(k, prefix)\n\t\t\tk = strings.TrimPrefix(k, \"_\")\n\t\t}\n\t\tif k != \"\" {\n\t\t\tkvs = append(kvs, &config.KeyValue{\n\t\t\t\tKey:   k,\n\t\t\t\tValue: []byte(v),\n\t\t\t})\n\t\t}\n\t}\n\treturn kvs\n}\n\nfunc (e *env) Watch() (config.Watcher, error) {\n\tw, err := NewWatcher()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn w, nil\n}\n\nfunc matchPrefix(prefixes []string, s string) (string, bool) {\n\tfor _, p := range prefixes {\n\t\tif strings.HasPrefix(s, p) {\n\t\t\treturn p, true\n\t\t}\n\t}\n\treturn \"\", false\n}\n"
  },
  {
    "path": "config/env/env_test.go",
    "content": "package env\n\nimport (\n\t\"os\"\n\t\"path/filepath\"\n\t\"reflect\"\n\t\"testing\"\n\n\t\"github.com/go-kratos/kratos/v2/config\"\n\t\"github.com/go-kratos/kratos/v2/config/file\"\n)\n\nconst _testJSON = `\n{\n    \"test\":{\n        \"server\":{\n            \"name\":\"${SERVICE_NAME}\",\n            \"addr\":\"${ADDR:127.0.0.1}\",\n            \"port\":\"${PORT:8080}\"\n        }\n    },\n    \"foo\":[\n        {\n            \"name\":\"Tom\",\n            \"age\":\"${AGE}\"\n        }\n    ]\n}`\n\nfunc TestEnvWithPrefix(t *testing.T) {\n\tvar (\n\t\tpath     = filepath.Join(t.TempDir(), \"test_config\")\n\t\tfilename = filepath.Join(path, \"test.json\")\n\t\tdata     = []byte(_testJSON)\n\t)\n\tdefer os.Remove(path)\n\tif err := os.MkdirAll(path, 0o700); err != nil {\n\t\tt.Error(err)\n\t}\n\tif err := os.WriteFile(filename, data, 0o666); err != nil {\n\t\tt.Error(err)\n\t}\n\n\t// set env\n\tprefix1, prefix2 := \"KRATOS_\", \"FOO\"\n\tenvs := map[string]string{\n\t\tprefix1 + \"SERVICE_NAME\": \"kratos_app\",\n\t\tprefix2 + \"ADDR\":         \"192.168.0.1\",\n\t\tprefix1 + \"AGE\":          \"20\",\n\t\t// only prefix\n\t\tprefix2:       \"foo\",\n\t\tprefix2 + \"_\": \"foo_\",\n\t}\n\n\tfor k, v := range envs {\n\t\tos.Setenv(k, v)\n\t}\n\n\tc := config.New(config.WithSource(\n\t\tfile.NewSource(path),\n\t\tNewSource(prefix1, prefix2),\n\t))\n\n\tif err := c.Load(); err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\ttests := []struct {\n\t\tname   string\n\t\tpath   string\n\t\texpect any\n\t}{\n\t\t{\n\t\t\tname:   \"test $KEY\",\n\t\t\tpath:   \"test.server.name\",\n\t\t\texpect: \"kratos_app\",\n\t\t},\n\t\t{\n\t\t\tname:   \"test ${KEY:DEFAULT} without default\",\n\t\t\tpath:   \"test.server.addr\",\n\t\t\texpect: \"192.168.0.1\",\n\t\t},\n\t\t{\n\t\t\tname:   \"test ${KEY:DEFAULT} with default\",\n\t\t\tpath:   \"test.server.port\",\n\t\t\texpect: \"8080\",\n\t\t},\n\t\t{\n\t\t\tname: \"test ${KEY} in array\",\n\t\t\tpath: \"foo\",\n\t\t\texpect: []any{\n\t\t\t\tmap[string]any{\n\t\t\t\t\t\"name\": \"Tom\",\n\t\t\t\t\t\"age\":  \"20\",\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t}\n\n\tfor _, test := range tests {\n\t\tt.Run(test.name, func(t *testing.T) {\n\t\t\tvar err error\n\t\t\tv := c.Value(test.path)\n\t\t\tif v.Load() != nil {\n\t\t\t\tvar actual any\n\t\t\t\tswitch test.expect.(type) {\n\t\t\t\tcase int:\n\t\t\t\t\tif actual, err = v.Int(); err == nil {\n\t\t\t\t\t\tif !reflect.DeepEqual(test.expect.(int), int(actual.(int64))) {\n\t\t\t\t\t\t\tt.Errorf(\"expect %v, actual %v\", test.expect, actual)\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\tcase string:\n\t\t\t\t\tif actual, err = v.String(); err == nil {\n\t\t\t\t\t\tif !reflect.DeepEqual(test.expect.(string), actual.(string)) {\n\t\t\t\t\t\t\tt.Errorf(`expect %v, actual %v`, test.expect, actual)\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\tcase bool:\n\t\t\t\t\tif actual, err = v.Bool(); err == nil {\n\t\t\t\t\t\tif !reflect.DeepEqual(test.expect.(bool), actual.(bool)) {\n\t\t\t\t\t\t\tt.Errorf(`expect %v, actual %v`, test.expect, actual)\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\tcase float64:\n\t\t\t\t\tif actual, err = v.Float(); err == nil {\n\t\t\t\t\t\tif !reflect.DeepEqual(test.expect.(float64), actual.(float64)) {\n\t\t\t\t\t\t\tt.Errorf(`expect %v, actual %v`, test.expect, actual)\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\tdefault:\n\t\t\t\t\tactual = v.Load()\n\t\t\t\t\tif !reflect.DeepEqual(test.expect, actual) {\n\t\t\t\t\t\tt.Logf(\"\\nexpect: %#v\\nactural: %#v\", test.expect, actual)\n\t\t\t\t\t\tt.Fail()\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif err != nil {\n\t\t\t\t\tt.Error(err)\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tt.Error(\"value path not found\")\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestEnvWithoutPrefix(t *testing.T) {\n\tvar (\n\t\tpath     = filepath.Join(t.TempDir(), \"test_config\")\n\t\tfilename = filepath.Join(path, \"test.json\")\n\t\tdata     = []byte(_testJSON)\n\t)\n\tdefer os.Remove(path)\n\tif err := os.MkdirAll(path, 0o700); err != nil {\n\t\tt.Error(err)\n\t}\n\tif err := os.WriteFile(filename, data, 0o666); err != nil {\n\t\tt.Error(err)\n\t}\n\n\t// set env\n\tenvs := map[string]string{\n\t\t\"SERVICE_NAME\": \"kratos_app\",\n\t\t\"ADDR\":         \"192.168.0.1\",\n\t\t\"AGE\":          \"20\",\n\t}\n\n\tfor k, v := range envs {\n\t\tos.Setenv(k, v)\n\t}\n\n\tc := config.New(config.WithSource(\n\t\tNewSource(),\n\t\tfile.NewSource(path),\n\t))\n\n\tif err := c.Load(); err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\ttests := []struct {\n\t\tname   string\n\t\tpath   string\n\t\texpect any\n\t}{\n\t\t{\n\t\t\tname:   \"test $KEY\",\n\t\t\tpath:   \"test.server.name\",\n\t\t\texpect: \"kratos_app\",\n\t\t},\n\t\t{\n\t\t\tname:   \"test ${KEY:DEFAULT} without default\",\n\t\t\tpath:   \"test.server.addr\",\n\t\t\texpect: \"192.168.0.1\",\n\t\t},\n\t\t{\n\t\t\tname:   \"test ${KEY:DEFAULT} with default\",\n\t\t\tpath:   \"test.server.port\",\n\t\t\texpect: \"8080\",\n\t\t},\n\t\t{\n\t\t\tname: \"test ${KEY} in array\",\n\t\t\tpath: \"foo\",\n\t\t\texpect: []any{\n\t\t\t\tmap[string]any{\n\t\t\t\t\t\"name\": \"Tom\",\n\t\t\t\t\t\"age\":  \"20\",\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t}\n\n\tfor _, test := range tests {\n\t\tt.Run(test.name, func(t *testing.T) {\n\t\t\tvar err error\n\t\t\tv := c.Value(test.path)\n\t\t\tif v.Load() != nil {\n\t\t\t\tvar actual any\n\t\t\t\tswitch test.expect.(type) {\n\t\t\t\tcase int:\n\t\t\t\t\tif actual, err = v.Int(); err == nil {\n\t\t\t\t\t\tif !reflect.DeepEqual(test.expect.(int), int(actual.(int64))) {\n\t\t\t\t\t\t\tt.Errorf(\"expect %v, actual %v\", test.expect, actual)\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\tcase string:\n\t\t\t\t\tif actual, err = v.String(); err == nil {\n\t\t\t\t\t\tif !reflect.DeepEqual(test.expect.(string), actual.(string)) {\n\t\t\t\t\t\t\tt.Errorf(`expect %v, actual %v`, test.expect, actual)\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\tcase bool:\n\t\t\t\t\tif actual, err = v.Bool(); err == nil {\n\t\t\t\t\t\tif !reflect.DeepEqual(test.expect.(bool), actual.(bool)) {\n\t\t\t\t\t\t\tt.Errorf(`expect %v, actual %v`, test.expect, actual)\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\tcase float64:\n\t\t\t\t\tif actual, err = v.Float(); err == nil {\n\t\t\t\t\t\tif !reflect.DeepEqual(test.expect.(float64), actual.(float64)) {\n\t\t\t\t\t\t\tt.Errorf(`expect %v, actual %v`, test.expect, actual)\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\tdefault:\n\t\t\t\t\tactual = v.Load()\n\t\t\t\t\tif !reflect.DeepEqual(test.expect, actual) {\n\t\t\t\t\t\tt.Logf(\"\\nexpect: %#v\\nactural: %#v\", test.expect, actual)\n\t\t\t\t\t\tt.Fail()\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif err != nil {\n\t\t\t\t\tt.Error(err)\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tt.Error(\"value path not found\")\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc Test_env_load(t *testing.T) {\n\ttype fields struct {\n\t\tprefixes []string\n\t}\n\ttype args struct {\n\t\tenvStrings []string\n\t}\n\ttests := []struct {\n\t\tname   string\n\t\tfields fields\n\t\targs   args\n\t\twant   []*config.KeyValue\n\t}{\n\t\t{\n\t\t\tname: \"without prefixes\",\n\t\t\tfields: fields{\n\t\t\t\tprefixes: nil,\n\t\t\t},\n\t\t\targs: args{\n\t\t\t\tenvStrings: []string{\n\t\t\t\t\t\"SERVICE_NAME=kratos_app\",\n\t\t\t\t\t\"ADDR=192.168.0.1\",\n\t\t\t\t\t\"AGE=20\",\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: []*config.KeyValue{\n\t\t\t\t{Key: \"SERVICE_NAME\", Value: []byte(\"kratos_app\"), Format: \"\"},\n\t\t\t\t{Key: \"ADDR\", Value: []byte(\"192.168.0.1\"), Format: \"\"},\n\t\t\t\t{Key: \"AGE\", Value: []byte(\"20\"), Format: \"\"},\n\t\t\t},\n\t\t},\n\n\t\t{\n\t\t\tname: \"empty prefix\",\n\t\t\tfields: fields{\n\t\t\t\tprefixes: []string{\"\"},\n\t\t\t},\n\t\t\targs: args{\n\t\t\t\tenvStrings: []string{\n\t\t\t\t\t\"__SERVICE_NAME=kratos_app\",\n\t\t\t\t\t\"__ADDR=192.168.0.1\",\n\t\t\t\t\t\"__AGE=20\",\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: []*config.KeyValue{\n\t\t\t\t{Key: \"_SERVICE_NAME\", Value: []byte(\"kratos_app\"), Format: \"\"},\n\t\t\t\t{Key: \"_ADDR\", Value: []byte(\"192.168.0.1\"), Format: \"\"},\n\t\t\t\t{Key: \"_AGE\", Value: []byte(\"20\"), Format: \"\"},\n\t\t\t},\n\t\t},\n\n\t\t{\n\t\t\tname: \"underscore prefix\",\n\t\t\tfields: fields{\n\t\t\t\tprefixes: []string{\"_\"},\n\t\t\t},\n\t\t\targs: args{\n\t\t\t\tenvStrings: []string{\n\t\t\t\t\t\"__SERVICE_NAME=kratos_app\",\n\t\t\t\t\t\"__ADDR=192.168.0.1\",\n\t\t\t\t\t\"__AGE=20\",\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: []*config.KeyValue{\n\t\t\t\t{Key: \"SERVICE_NAME\", Value: []byte(\"kratos_app\"), Format: \"\"},\n\t\t\t\t{Key: \"ADDR\", Value: []byte(\"192.168.0.1\"), Format: \"\"},\n\t\t\t\t{Key: \"AGE\", Value: []byte(\"20\"), Format: \"\"},\n\t\t\t},\n\t\t},\n\n\t\t{\n\t\t\tname: \"with prefixes\",\n\t\t\tfields: fields{\n\t\t\t\tprefixes: []string{\"KRATOS_\", \"FOO\"},\n\t\t\t},\n\t\t\targs: args{\n\t\t\t\tenvStrings: []string{\n\t\t\t\t\t\"KRATOS_SERVICE_NAME=kratos_app\",\n\t\t\t\t\t\"KRATOS_ADDR=192.168.0.1\",\n\t\t\t\t\t\"FOO_AGE=20\",\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: []*config.KeyValue{\n\t\t\t\t{Key: \"SERVICE_NAME\", Value: []byte(\"kratos_app\"), Format: \"\"},\n\t\t\t\t{Key: \"ADDR\", Value: []byte(\"192.168.0.1\"), Format: \"\"},\n\t\t\t\t{Key: \"AGE\", Value: []byte(\"20\"), Format: \"\"},\n\t\t\t},\n\t\t},\n\n\t\t{\n\t\t\tname: \"should not panic #1\",\n\t\t\tfields: fields{\n\t\t\t\tprefixes: []string{\"FOO\"},\n\t\t\t},\n\t\t\targs: args{\n\t\t\t\tenvStrings: []string{\n\t\t\t\t\t\"FOO=123\",\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: nil,\n\t\t},\n\n\t\t{\n\t\t\tname: \"should not panic #2\",\n\t\t\tfields: fields{\n\t\t\t\tprefixes: []string{\"FOO=1\"},\n\t\t\t},\n\t\t\targs: args{\n\t\t\t\tenvStrings: []string{\n\t\t\t\t\t\"FOO=123\",\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: nil,\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\te := &env{\n\t\t\t\tprefixes: tt.fields.prefixes,\n\t\t\t}\n\t\t\tgot := e.load(tt.args.envStrings)\n\t\t\tif !reflect.DeepEqual(tt.want, got) {\n\t\t\t\tt.Errorf(\"env.load() = %v, want %v\", got, tt.want)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc Test_matchPrefix(t *testing.T) {\n\ttype args struct {\n\t\tprefixes []string\n\t\ts        string\n\t}\n\ttests := []struct {\n\t\tname   string\n\t\targs   args\n\t\twant   string\n\t\twantOk bool\n\t}{\n\t\t{args: args{prefixes: nil, s: \"foo=123\"}, want: \"\", wantOk: false},\n\t\t{args: args{prefixes: []string{\"\"}, s: \"foo=123\"}, want: \"\", wantOk: true},\n\t\t{args: args{prefixes: []string{\"foo\"}, s: \"foo=123\"}, want: \"foo\", wantOk: true},\n\t\t{args: args{prefixes: []string{\"foo=1\"}, s: \"foo=123\"}, want: \"foo=1\", wantOk: true},\n\t\t{args: args{prefixes: []string{\"foo=1234\"}, s: \"foo=123\"}, want: \"\", wantOk: false},\n\t\t{args: args{prefixes: []string{\"bar\"}, s: \"foo=123\"}, want: \"\", wantOk: false},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tgot, gotOk := matchPrefix(tt.args.prefixes, tt.args.s)\n\t\t\tif got != tt.want {\n\t\t\t\tt.Errorf(\"matchPrefix() got = %v, want %v\", got, tt.want)\n\t\t\t}\n\t\t\tif gotOk != tt.wantOk {\n\t\t\t\tt.Errorf(\"matchPrefix() gotOk = %v, wantOk %v\", gotOk, tt.wantOk)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc Test_env_watch(t *testing.T) {\n\tprefixes := []string{\"BAR\", \"FOO\"}\n\tsource := NewSource(prefixes...)\n\tw, err := source.Watch()\n\tif err != nil {\n\t\tt.Errorf(\"expect no err, got %v\", err)\n\t}\n\t_ = w.Stop()\n}\n"
  },
  {
    "path": "config/env/watcher.go",
    "content": "package env\n\nimport (\n\t\"context\"\n\n\t\"github.com/go-kratos/kratos/v2/config\"\n)\n\nvar _ config.Watcher = (*watcher)(nil)\n\ntype watcher struct {\n\tctx    context.Context\n\tcancel context.CancelFunc\n}\n\nfunc NewWatcher() (config.Watcher, error) {\n\tctx, cancel := context.WithCancel(context.Background())\n\treturn &watcher{ctx: ctx, cancel: cancel}, nil\n}\n\n// Next will be blocked until the Stop method is called\nfunc (w *watcher) Next() ([]*config.KeyValue, error) {\n\t<-w.ctx.Done()\n\treturn nil, w.ctx.Err()\n}\n\nfunc (w *watcher) Stop() error {\n\tw.cancel()\n\treturn nil\n}\n"
  },
  {
    "path": "config/env/watcher_test.go",
    "content": "package env\n\nimport (\n\t\"testing\"\n)\n\nfunc Test_watcher_next(t *testing.T) {\n\tt.Run(\"next after stop should return err\", func(t *testing.T) {\n\t\tw, err := NewWatcher()\n\t\tif err != nil {\n\t\t\tt.Errorf(\"expect no error, got %v\", err)\n\t\t}\n\n\t\t_ = w.Stop()\n\t\t_, err = w.Next()\n\t\tif err == nil {\n\t\t\tt.Error(\"expect error, actual nil\")\n\t\t}\n\t})\n}\n\nfunc Test_watcher_stop(t *testing.T) {\n\tt.Run(\"stop multiple times should not panic\", func(t *testing.T) {\n\t\tw, err := NewWatcher()\n\t\tif err != nil {\n\t\t\tt.Errorf(\"expect no error, got %v\", err)\n\t\t}\n\n\t\t_ = w.Stop()\n\t\t_ = w.Stop()\n\t})\n}\n"
  },
  {
    "path": "config/file/file.go",
    "content": "package file\n\nimport (\n\t\"io\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"strings\"\n\n\t\"github.com/go-kratos/kratos/v2/config\"\n)\n\nvar _ config.Source = (*file)(nil)\n\ntype file struct {\n\tpath string\n}\n\n// NewSource new a file source.\nfunc NewSource(path string) config.Source {\n\treturn &file{path: path}\n}\n\nfunc (f *file) loadFile(path string) (*config.KeyValue, error) {\n\tfile, err := os.Open(path)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdefer file.Close()\n\tdata, err := io.ReadAll(file)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tinfo, err := file.Stat()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn &config.KeyValue{\n\t\tKey:    info.Name(),\n\t\tFormat: format(info.Name()),\n\t\tValue:  data,\n\t}, nil\n}\n\nfunc (f *file) loadDir(path string) (kvs []*config.KeyValue, err error) {\n\tfiles, err := os.ReadDir(path)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tfor _, file := range files {\n\t\t// ignore hidden files\n\t\tif file.IsDir() || strings.HasPrefix(file.Name(), \".\") {\n\t\t\tcontinue\n\t\t}\n\t\tkv, err := f.loadFile(filepath.Join(path, file.Name()))\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tkvs = append(kvs, kv)\n\t}\n\treturn\n}\n\nfunc (f *file) Load() (kvs []*config.KeyValue, err error) {\n\tfi, err := os.Stat(f.path)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif fi.IsDir() {\n\t\treturn f.loadDir(f.path)\n\t}\n\tkv, err := f.loadFile(f.path)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn []*config.KeyValue{kv}, nil\n}\n\nfunc (f *file) Watch() (config.Watcher, error) {\n\treturn newWatcher(f)\n}\n"
  },
  {
    "path": "config/file/file_test.go",
    "content": "package file\n\nimport (\n\t\"errors\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"reflect\"\n\t\"sync\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/go-kratos/kratos/v2/config\"\n)\n\nconst (\n\t_testJSON = `\n{\n    \"test\":{\n        \"settings\":{\n            \"int_key\":1000,\n            \"float_key\":1000.1,\n            \"duration_key\":10000,\n            \"string_key\":\"string_value\"\n        },\n        \"server\":{\n            \"addr\":\"127.0.0.1\",\n            \"port\":8000\n        }\n    },\n    \"foo\":[\n        {\n            \"name\":\"nihao\",\n            \"age\":18\n        },\n        {\n            \"name\":\"nihao\",\n            \"age\":18\n        }\n    ]\n}`\n\n\t_testJSONUpdate = `\n{\n    \"test\":{\n        \"settings\":{\n            \"int_key\":1000,\n            \"float_key\":1000.1,\n            \"duration_key\":10000,\n            \"string_key\":\"string_value\"\n        },\n        \"server\":{\n            \"addr\":\"127.0.0.1\",\n            \"port\":8000\n        }\n    },\n    \"foo\":[\n        {\n            \"name\":\"nihao\",\n            \"age\":18\n        },\n        {\n            \"name\":\"nihao\",\n            \"age\":18\n        }\n    ],\n    \"bar\":{\n        \"event\":\"update\"\n    }\n}`\n\n\t//\t_testYaml = `\n\t//Foo:\n\t//    bar :\n\t//        - {name: nihao,age: 1}\n\t//        - {name: nihao,age: 1}\n\t//\n\t//\n\t//`\n)\n\n//func TestScan(t *testing.T) {\n//\n//}\n\nfunc TestFile(t *testing.T) {\n\tvar (\n\t\tpath = filepath.Join(t.TempDir(), \"test_config\")\n\t\tfile = filepath.Join(path, \"test.json\")\n\t\tdata = []byte(_testJSON)\n\t)\n\tdefer os.Remove(path)\n\tif err := os.MkdirAll(path, 0o700); err != nil {\n\t\tt.Error(err)\n\t}\n\tif err := os.WriteFile(file, data, 0o666); err != nil {\n\t\tt.Error(err)\n\t}\n\ttestSource(t, file, data)\n\ttestSource(t, path, data)\n\ttestWatchFile(t, file)\n\ttestWatchDir(t, path, file)\n}\n\nfunc testWatchFile(t *testing.T, path string) {\n\tt.Log(path)\n\n\ts := NewSource(path)\n\twatch, err := s.Watch()\n\tif err != nil {\n\t\tt.Error(err)\n\t}\n\n\tf, err := os.OpenFile(path, os.O_RDWR, 0)\n\tif err != nil {\n\t\tt.Error(err)\n\t}\n\tdefer f.Close()\n\t_, err = f.WriteString(_testJSONUpdate)\n\tif err != nil {\n\t\tt.Error(err)\n\t}\n\tkvs, err := watch.Next()\n\tif err != nil {\n\t\tt.Errorf(\"watch.Next() error(%v)\", err)\n\t}\n\tif !reflect.DeepEqual(string(kvs[0].Value), _testJSONUpdate) {\n\t\tt.Errorf(\"string(kvs[0].Value(%v) is  not equal to _testJSONUpdate(%v)\", kvs[0].Value, _testJSONUpdate)\n\t}\n\n\tnewFilepath := filepath.Join(filepath.Dir(path), \"test1.json\")\n\tif err = os.Rename(path, newFilepath); err != nil {\n\t\tt.Error(err)\n\t}\n\tkvs, err = watch.Next()\n\tif err == nil {\n\t\tt.Errorf(\"watch.Next() error(%v)\", err)\n\t}\n\tif kvs != nil {\n\t\tt.Errorf(\"watch.Next() error(%v)\", err)\n\t}\n\n\terr = watch.Stop()\n\tif err != nil {\n\t\tt.Errorf(\"watch.Stop() error(%v)\", err)\n\t}\n\n\tif err := os.Rename(newFilepath, path); err != nil {\n\t\tt.Error(err)\n\t}\n}\n\nfunc testWatchDir(t *testing.T, path, file string) {\n\tt.Log(path)\n\tt.Log(file)\n\n\ts := NewSource(path)\n\twatch, err := s.Watch()\n\tif err != nil {\n\t\tt.Error(err)\n\t}\n\n\tf, err := os.OpenFile(file, os.O_RDWR, 0)\n\tif err != nil {\n\t\tt.Error(err)\n\t}\n\tdefer f.Close()\n\t_, err = f.WriteString(_testJSONUpdate)\n\tif err != nil {\n\t\tt.Error(err)\n\t}\n\n\tkvs, err := watch.Next()\n\tif err != nil {\n\t\tt.Errorf(\"watch.Next() error(%v)\", err)\n\t}\n\tif !reflect.DeepEqual(string(kvs[0].Value), _testJSONUpdate) {\n\t\tt.Errorf(\"string(kvs[0].Value(%s) is  not equal to _testJSONUpdate(%v)\", kvs[0].Value, _testJSONUpdate)\n\t}\n}\n\nfunc testSource(t *testing.T, path string, data []byte) {\n\tt.Log(path)\n\n\ts := NewSource(path)\n\tkvs, err := s.Load()\n\tif err != nil {\n\t\tt.Error(err)\n\t}\n\tif string(kvs[0].Value) != string(data) {\n\t\tt.Errorf(\"no expected: %s, but got: %s\", kvs[0].Value, data)\n\t}\n}\n\nfunc TestConfig(t *testing.T) {\n\tpath := filepath.Join(t.TempDir(), \"test_config.json\")\n\tdefer os.Remove(path)\n\tif err := os.WriteFile(path, []byte(_testJSON), 0o666); err != nil {\n\t\tt.Error(err)\n\t}\n\tc := config.New(config.WithSource(\n\t\tNewSource(path),\n\t))\n\ttestScan(t, c)\n\n\ttestConfig(t, c)\n}\n\nfunc testConfig(t *testing.T, c config.Config) {\n\texpected := map[string]any{\n\t\t\"test.settings.int_key\":      int64(1000),\n\t\t\"test.settings.float_key\":    1000.1,\n\t\t\"test.settings.string_key\":   \"string_value\",\n\t\t\"test.settings.duration_key\": time.Duration(10000),\n\t\t\"test.server.addr\":           \"127.0.0.1\",\n\t\t\"test.server.port\":           int64(8000),\n\t}\n\tif err := c.Load(); err != nil {\n\t\tt.Error(err)\n\t}\n\tfor key, value := range expected {\n\t\tswitch value.(type) {\n\t\tcase int64:\n\t\t\tif v, err := c.Value(key).Int(); err != nil {\n\t\t\t\tt.Error(key, value, err)\n\t\t\t} else if v != value {\n\t\t\t\tt.Errorf(\"no expect key: %s value: %v, but got: %v\", key, value, v)\n\t\t\t}\n\t\tcase float64:\n\t\t\tif v, err := c.Value(key).Float(); err != nil {\n\t\t\t\tt.Error(key, value, err)\n\t\t\t} else if v != value {\n\t\t\t\tt.Errorf(\"no expect key: %s value: %v, but got: %v\", key, value, v)\n\t\t\t}\n\t\tcase string:\n\t\t\tif v, err := c.Value(key).String(); err != nil {\n\t\t\t\tt.Error(key, value, err)\n\t\t\t} else if v != value {\n\t\t\t\tt.Errorf(\"no expect key: %s value: %v, but got: %v\", key, value, v)\n\t\t\t}\n\t\tcase time.Duration:\n\t\t\tif v, err := c.Value(key).Duration(); err != nil {\n\t\t\t\tt.Error(key, value, err)\n\t\t\t} else if v != value {\n\t\t\t\tt.Errorf(\"no expect key: %s value: %v, but got: %v\", key, value, v)\n\t\t\t}\n\t\t}\n\t}\n\t// scan\n\tvar settings struct {\n\t\tIntKey      int64         `json:\"int_key\"`\n\t\tFloatKey    float64       `json:\"float_key\"`\n\t\tStringKey   string        `json:\"string_key\"`\n\t\tDurationKey time.Duration `json:\"duration_key\"`\n\t}\n\tif err := c.Value(\"test.settings\").Scan(&settings); err != nil {\n\t\tt.Error(err)\n\t}\n\tif v := expected[\"test.settings.int_key\"]; settings.IntKey != v {\n\t\tt.Errorf(\"no expect int_key value: %v, but got: %v\", settings.IntKey, v)\n\t}\n\tif v := expected[\"test.settings.float_key\"]; settings.FloatKey != v {\n\t\tt.Errorf(\"no expect float_key value: %v, but got: %v\", settings.FloatKey, v)\n\t}\n\tif v := expected[\"test.settings.string_key\"]; settings.StringKey != v {\n\t\tt.Errorf(\"no expect string_key value: %v, but got: %v\", settings.StringKey, v)\n\t}\n\tif v := expected[\"test.settings.duration_key\"]; settings.DurationKey != v {\n\t\tt.Errorf(\"no expect duration_key value: %v, but got: %v\", settings.DurationKey, v)\n\t}\n\n\t// not found\n\tif _, err := c.Value(\"not_found_key\").Bool(); errors.Is(err, config.ErrNotFound) {\n\t\tt.Logf(\"not_found_key not match: %v\", err)\n\t}\n}\n\nfunc testScan(t *testing.T, c config.Config) {\n\ttype TestJSON struct {\n\t\tTest struct {\n\t\t\tSettings struct {\n\t\t\t\tIntKey      int     `json:\"int_key\"`\n\t\t\t\tFloatKey    float64 `json:\"float_key\"`\n\t\t\t\tDurationKey int     `json:\"duration_key\"`\n\t\t\t\tStringKey   string  `json:\"string_key\"`\n\t\t\t} `json:\"settings\"`\n\t\t\tServer struct {\n\t\t\t\tAddr string `json:\"addr\"`\n\t\t\t\tPort int    `json:\"port\"`\n\t\t\t} `json:\"server\"`\n\t\t} `json:\"test\"`\n\t\tFoo []struct {\n\t\t\tName string `json:\"name\"`\n\t\t\tAge  int    `json:\"age\"`\n\t\t} `json:\"foo\"`\n\t}\n\tvar conf TestJSON\n\tif err := c.Load(); err != nil {\n\t\tt.Error(err)\n\t}\n\tif err := c.Scan(&conf); err != nil {\n\t\tt.Error(err)\n\t}\n\tt.Log(conf)\n}\n\nfunc TestMergeDataRace(t *testing.T) {\n\tpath := filepath.Join(t.TempDir(), \"test_config.json\")\n\tdefer os.Remove(path)\n\tif err := os.WriteFile(path, []byte(_testJSON), 0o666); err != nil {\n\t\tt.Error(err)\n\t}\n\tc := config.New(config.WithSource(\n\t\tNewSource(path),\n\t))\n\tconst count = 80\n\twg := &sync.WaitGroup{}\n\twg.Add(2)\n\tstartCh := make(chan struct{})\n\tgo func() {\n\t\tdefer wg.Done()\n\t\t<-startCh\n\t\tfor i := 0; i < count; i++ {\n\t\t\tvar conf struct{}\n\t\t\tif err := c.Scan(&conf); err != nil {\n\t\t\t\tt.Error(err)\n\t\t\t}\n\t\t}\n\t}()\n\n\tgo func() {\n\t\tdefer wg.Done()\n\t\t<-startCh\n\t\tfor i := 0; i < count; i++ {\n\t\t\tif err := c.Load(); err != nil {\n\t\t\t\tt.Error(err)\n\t\t\t}\n\t\t}\n\t}()\n\tclose(startCh)\n\twg.Wait()\n}\n"
  },
  {
    "path": "config/file/format.go",
    "content": "package file\n\nimport \"strings\"\n\nfunc format(name string) string {\n\tif idx := strings.LastIndexByte(name, '.'); idx >= 0 {\n\t\treturn name[idx+1:]\n\t}\n\treturn \"\"\n}\n"
  },
  {
    "path": "config/file/format_test.go",
    "content": "package file\n\nimport (\n\t\"testing\"\n)\n\nfunc TestFormat(t *testing.T) {\n\ttests := []struct {\n\t\tinput  string\n\t\texpect string\n\t}{\n\t\t{\n\t\t\tinput:  \"\",\n\t\t\texpect: \"\",\n\t\t},\n\t\t{\n\t\t\tinput:  \" \",\n\t\t\texpect: \"\",\n\t\t},\n\t\t{\n\t\t\tinput:  \".\",\n\t\t\texpect: \"\",\n\t\t},\n\t\t{\n\t\t\tinput:  \"a\",\n\t\t\texpect: \"\",\n\t\t},\n\t\t{\n\t\t\tinput:  \"a.\",\n\t\t\texpect: \"\",\n\t\t},\n\t\t{\n\t\t\tinput:  \".b\",\n\t\t\texpect: \"b\",\n\t\t},\n\t\t{\n\t\t\tinput:  \"a.b\",\n\t\t\texpect: \"b\",\n\t\t},\n\t\t{\n\t\t\tinput:  \"a.b.c\",\n\t\t\texpect: \"c\",\n\t\t},\n\t}\n\tfor _, v := range tests {\n\t\tcontent := format(v.input)\n\t\tif got, want := content, v.expect; got != want {\n\t\t\tt.Errorf(\"expect %v,got %v\", want, got)\n\t\t}\n\t}\n}\n\nfunc BenchmarkFormat(b *testing.B) {\n\tfor i := 0; i < b.N; i++ {\n\t\tformat(\"abc.txt\")\n\t}\n}\n"
  },
  {
    "path": "config/file/watcher.go",
    "content": "package file\n\nimport (\n\t\"context\"\n\t\"os\"\n\t\"path/filepath\"\n\n\t\"github.com/fsnotify/fsnotify\"\n\n\t\"github.com/go-kratos/kratos/v2/config\"\n)\n\nvar _ config.Watcher = (*watcher)(nil)\n\ntype watcher struct {\n\tf  *file\n\tfw *fsnotify.Watcher\n\n\tctx    context.Context\n\tcancel context.CancelFunc\n}\n\nfunc newWatcher(f *file) (config.Watcher, error) {\n\tfw, err := fsnotify.NewWatcher()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif err := fw.Add(f.path); err != nil {\n\t\treturn nil, err\n\t}\n\tctx, cancel := context.WithCancel(context.Background())\n\treturn &watcher{f: f, fw: fw, ctx: ctx, cancel: cancel}, nil\n}\n\nfunc (w *watcher) Next() ([]*config.KeyValue, error) {\n\tselect {\n\tcase <-w.ctx.Done():\n\t\treturn nil, w.ctx.Err()\n\tcase event := <-w.fw.Events:\n\t\tif event.Op == fsnotify.Rename {\n\t\t\tif _, err := os.Stat(event.Name); err == nil || os.IsExist(err) {\n\t\t\t\tif err := w.fw.Add(event.Name); err != nil {\n\t\t\t\t\treturn nil, err\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tfi, err := os.Stat(w.f.path)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tpath := w.f.path\n\t\tif fi.IsDir() {\n\t\t\tpath = filepath.Join(w.f.path, filepath.Base(event.Name))\n\t\t}\n\t\tkv, err := w.f.loadFile(path)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\treturn []*config.KeyValue{kv}, nil\n\tcase err := <-w.fw.Errors:\n\t\treturn nil, err\n\t}\n}\n\nfunc (w *watcher) Stop() error {\n\tw.cancel()\n\treturn w.fw.Close()\n}\n"
  },
  {
    "path": "config/options.go",
    "content": "package config\n\nimport (\n\t\"fmt\"\n\t\"regexp\"\n\t\"strconv\"\n\t\"strings\"\n\n\t\"github.com/go-kratos/kratos/v2/encoding\"\n)\n\n// Decoder is config decoder.\ntype Decoder func(*KeyValue, map[string]any) error\n\n// Resolver resolve placeholder in config.\ntype Resolver func(map[string]any) error\n\n// Merge is config merge func.\ntype Merge func(dst, src any) error\n\n// Option is config option.\ntype Option func(*options)\n\ntype options struct {\n\tsources  []Source\n\tdecoder  Decoder\n\tresolver Resolver\n\tmerge    Merge\n}\n\n// WithSource with config source.\nfunc WithSource(s ...Source) Option {\n\treturn func(o *options) {\n\t\to.sources = s\n\t}\n}\n\n// WithDecoder with config decoder.\n// DefaultDecoder behavior:\n// If KeyValue.Format is non-empty, then KeyValue.Value will be deserialized into map[string]interface{}\n// and stored in the config cache(map[string]interface{})\n// if KeyValue.Format is empty,{KeyValue.Key : KeyValue.Value} will be stored in config cache(map[string]interface{})\nfunc WithDecoder(d Decoder) Option {\n\treturn func(o *options) {\n\t\to.decoder = d\n\t}\n}\n\n// WithResolveActualTypes with config resolver.\n// bool input will enable conversion of config to data types\nfunc WithResolveActualTypes(enableConvertToType bool) Option {\n\treturn func(o *options) {\n\t\to.resolver = newActualTypesResolver(enableConvertToType)\n\t}\n}\n\n// WithResolver with config resolver.\nfunc WithResolver(r Resolver) Option {\n\treturn func(o *options) {\n\t\to.resolver = r\n\t}\n}\n\n// WithMergeFunc with config merge func.\nfunc WithMergeFunc(m Merge) Option {\n\treturn func(o *options) {\n\t\to.merge = m\n\t}\n}\n\n// defaultDecoder decode config from source KeyValue\n// to target map[string]interface{} using src.Format codec.\nfunc defaultDecoder(src *KeyValue, target map[string]any) error {\n\tif src.Format == \"\" {\n\t\t// expand key \"aaa.bbb\" into map[aaa]map[bbb]interface{}\n\t\tkeys := strings.Split(src.Key, \".\")\n\t\tfor i, k := range keys {\n\t\t\tif i == len(keys)-1 {\n\t\t\t\ttarget[k] = src.Value\n\t\t\t} else {\n\t\t\t\tsub := make(map[string]any)\n\t\t\t\ttarget[k] = sub\n\t\t\t\ttarget = sub\n\t\t\t}\n\t\t}\n\t\treturn nil\n\t}\n\tif codec := encoding.GetCodec(src.Format); codec != nil {\n\t\treturn codec.Unmarshal(src.Value, &target)\n\t}\n\treturn fmt.Errorf(\"unsupported key: %s format: %s\", src.Key, src.Format)\n}\n\nfunc newActualTypesResolver(enableConvertToType bool) func(map[string]any) error {\n\treturn func(input map[string]any) error {\n\t\tmapper := mapper(input)\n\t\treturn resolver(input, mapper, enableConvertToType)\n\t}\n}\n\n// defaultResolver resolve placeholder in map value,\n// placeholder format in ${key:default}.\nfunc defaultResolver(input map[string]any) error {\n\tmapper := mapper(input)\n\treturn resolver(input, mapper, false)\n}\n\nfunc resolver(input map[string]any, mapper func(name string) string, toType bool) error {\n\tvar resolve func(map[string]any) error\n\tresolve = func(sub map[string]any) error {\n\t\tfor k, v := range sub {\n\t\t\tswitch vt := v.(type) {\n\t\t\tcase string:\n\t\t\t\tsub[k] = expand(vt, mapper, toType)\n\t\t\tcase map[string]any:\n\t\t\t\tif err := resolve(vt); err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\tcase []any:\n\t\t\t\tfor i, iface := range vt {\n\t\t\t\t\tswitch it := iface.(type) {\n\t\t\t\t\tcase string:\n\t\t\t\t\t\tvt[i] = expand(it, mapper, toType)\n\t\t\t\t\tcase map[string]any:\n\t\t\t\t\t\tif err := resolve(it); err != nil {\n\t\t\t\t\t\t\treturn err\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tsub[k] = vt\n\t\t\t}\n\t\t}\n\t\treturn nil\n\t}\n\treturn resolve(input)\n}\n\nfunc mapper(input map[string]any) func(name string) string {\n\tmapper := func(name string) string {\n\t\targs := strings.SplitN(strings.TrimSpace(name), \":\", 2) //nolint:mnd\n\t\tif v, has := readValue(input, args[0]); has {\n\t\t\ts, _ := v.String()\n\t\t\treturn s\n\t\t} else if len(args) > 1 { // default value\n\t\t\treturn args[1]\n\t\t}\n\t\treturn \"\"\n\t}\n\treturn mapper\n}\n\nfunc convertToType(input string) any {\n\t// Check if the input is a string with quotes\n\tif strings.HasPrefix(input, \"\\\"\") && strings.HasSuffix(input, \"\\\"\") {\n\t\t// Trim the quotes and return the string value\n\t\treturn strings.Trim(input, \"\\\"\")\n\t}\n\n\t// Try converting to bool\n\tif input == \"true\" || input == \"false\" {\n\t\tb, _ := strconv.ParseBool(input)\n\t\treturn b\n\t}\n\n\t// Try converting to float64\n\tif strings.Contains(input, \".\") {\n\t\tif f, err := strconv.ParseFloat(input, 64); err == nil {\n\t\t\treturn f\n\t\t}\n\t}\n\n\t// Try converting to int64\n\tif i, err := strconv.ParseInt(input, 10, 64); err == nil {\n\t\treturn i\n\t}\n\n\t// Default to string if no other conversion succeeds\n\treturn input\n}\n\n// placeholderRegexp matches ${...} placeholders in config value\nvar placeholderRegexp = regexp.MustCompile(`\\${(.*?)}`)\n\nfunc expand(s string, mapping func(string) string, toType bool) any {\n\tre := placeholderRegexp.FindAllStringSubmatch(s, -1)\n\tvar ct any\n\tfor _, i := range re {\n\t\tif len(i) == 2 { //nolint:mnd\n\t\t\tm := mapping(i[1])\n\t\t\tif toType {\n\t\t\t\tct = convertToType(m)\n\t\t\t\treturn ct\n\t\t\t}\n\t\t\ts = strings.ReplaceAll(s, i[0], m)\n\t\t}\n\t}\n\treturn s\n}\n"
  },
  {
    "path": "config/options_test.go",
    "content": "package config\n\nimport (\n\t\"reflect\"\n\t\"strings\"\n\t\"testing\"\n)\n\nfunc TestDefaultDecoder(t *testing.T) {\n\tsrc := &KeyValue{\n\t\tKey:    \"service\",\n\t\tValue:  []byte(\"config\"),\n\t\tFormat: \"\",\n\t}\n\ttarget := make(map[string]any)\n\terr := defaultDecoder(src, target)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif !reflect.DeepEqual(target, map[string]any{\"service\": []byte(\"config\")}) {\n\t\tt.Fatal(`target is not equal to map[string]interface{}{\"service\": \"config\"}`)\n\t}\n\n\tsrc = &KeyValue{\n\t\tKey:    \"service.name.alias\",\n\t\tValue:  []byte(\"2233\"),\n\t\tFormat: \"\",\n\t}\n\ttarget = make(map[string]any)\n\terr = defaultDecoder(src, target)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif !reflect.DeepEqual(map[string]any{\n\t\t\"service\": map[string]any{\n\t\t\t\"name\": map[string]any{\n\t\t\t\t\"alias\": []byte(\"2233\"),\n\t\t\t},\n\t\t},\n\t}, target) {\n\t\tt.Fatal(`target is not equal to map[string]interface{}{\"service\": map[string]interface{}{\"name\": map[string]interface{}{\"alias\": []byte(\"2233\")}}}`)\n\t}\n}\n\nfunc TestDefaultResolver(t *testing.T) {\n\tvar (\n\t\tportString = \"8080\"\n\t\tcountInt   = 10\n\t\trateFloat  = 0.9\n\t)\n\n\tdata := map[string]any{\n\t\t\"foo\": map[string]any{\n\t\t\t\"bar\": map[string]any{\n\t\t\t\t\"notexist\": \"${NOTEXIST:100}\",\n\t\t\t\t\"port\":     \"${PORT:8081}\",\n\t\t\t\t\"count\":    \"${COUNT:0}\",\n\t\t\t\t\"enable\":   \"${ENABLE:false}\",\n\t\t\t\t\"rate\":     \"${RATE}\",\n\t\t\t\t\"empty\":    \"${EMPTY:foobar}\",\n\t\t\t\t\"url\":      \"${URL:http://example.com}\",\n\t\t\t\t\"array\": []any{\n\t\t\t\t\t\"${PORT}\",\n\t\t\t\t\tmap[string]any{\"foobar\": \"${NOTEXIST:8081}\"},\n\t\t\t\t},\n\t\t\t\t\"value1\": \"${test.value}\",\n\t\t\t\t\"value2\": \"$PORT\",\n\t\t\t\t\"value3\": \"abc${PORT}foo${COUNT}bar\",\n\t\t\t\t\"value4\": \"${foo${bar}}\",\n\t\t\t},\n\t\t},\n\t\t\"test\": map[string]any{\n\t\t\t\"value\": \"foobar\",\n\t\t},\n\t\t\"PORT\":   \"8080\",\n\t\t\"COUNT\":  \"10\",\n\t\t\"ENABLE\": \"true\",\n\t\t\"RATE\":   \"0.9\",\n\t\t\"EMPTY\":  \"\",\n\t}\n\n\ttests := []struct {\n\t\tname   string\n\t\tpath   string\n\t\texpect any\n\t}{\n\t\t{\n\t\t\tname:   \"test not exist int env with default\",\n\t\t\tpath:   \"foo.bar.notexist\",\n\t\t\texpect: 100,\n\t\t},\n\t\t{\n\t\t\tname:   \"test string with default\",\n\t\t\tpath:   \"foo.bar.port\",\n\t\t\texpect: portString,\n\t\t},\n\t\t{\n\t\t\tname:   \"test int with default\",\n\t\t\tpath:   \"foo.bar.count\",\n\t\t\texpect: countInt,\n\t\t},\n\t\t{\n\t\t\tname:   \"test bool with default\",\n\t\t\tpath:   \"foo.bar.enable\",\n\t\t\texpect: true,\n\t\t},\n\t\t{\n\t\t\tname:   \"test float without default\",\n\t\t\tpath:   \"foo.bar.rate\",\n\t\t\texpect: rateFloat,\n\t\t},\n\t\t{\n\t\t\tname:   \"test empty value with default\",\n\t\t\tpath:   \"foo.bar.empty\",\n\t\t\texpect: \"\",\n\t\t},\n\t\t{\n\t\t\tname:   \"test url with default\",\n\t\t\tpath:   \"foo.bar.url\",\n\t\t\texpect: \"http://example.com\",\n\t\t},\n\t\t{\n\t\t\tname:   \"test array\",\n\t\t\tpath:   \"foo.bar.array\",\n\t\t\texpect: []any{portString, map[string]any{\"foobar\": \"8081\"}},\n\t\t},\n\t\t{\n\t\t\tname:   \"test ${test.value}\",\n\t\t\tpath:   \"foo.bar.value1\",\n\t\t\texpect: \"foobar\",\n\t\t},\n\t\t{\n\t\t\tname:   \"test $PORT\",\n\t\t\tpath:   \"foo.bar.value2\",\n\t\t\texpect: \"$PORT\",\n\t\t},\n\t\t{\n\t\t\tname:   \"test abc${PORT}foo${COUNT}bar\",\n\t\t\tpath:   \"foo.bar.value3\",\n\t\t\texpect: \"abc8080foo10bar\",\n\t\t},\n\t\t{\n\t\t\tname:   \"test ${foo${bar}}\",\n\t\t\tpath:   \"foo.bar.value4\",\n\t\t\texpect: \"}\",\n\t\t},\n\t}\n\n\tfor _, test := range tests {\n\t\tt.Run(test.name, func(t *testing.T) {\n\t\t\terr := defaultResolver(data)\n\t\t\tif err != nil {\n\t\t\t\tt.Fatal(err)\n\t\t\t}\n\t\t\trd := reader{\n\t\t\t\tvalues: data,\n\t\t\t}\n\t\t\tif v, ok := rd.Value(test.path); ok {\n\t\t\t\tvar actual any\n\t\t\t\tswitch test.expect.(type) {\n\t\t\t\tcase int:\n\t\t\t\t\tif actual, err = v.Int(); err == nil {\n\t\t\t\t\t\tif !reflect.DeepEqual(test.expect.(int), int(actual.(int64))) {\n\t\t\t\t\t\t\tt.Fatal(\"expect is not equal to actual\")\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\tcase string:\n\t\t\t\t\tif actual, err = v.String(); err == nil {\n\t\t\t\t\t\tif !reflect.DeepEqual(test.expect, actual) {\n\t\t\t\t\t\t\tt.Fatal(\"expect is not equal to actual\")\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\tcase bool:\n\t\t\t\t\tif actual, err = v.Bool(); err == nil {\n\t\t\t\t\t\tif !reflect.DeepEqual(test.expect, actual) {\n\t\t\t\t\t\t\tt.Fatal(\"expect is not equal to actual\")\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\tcase float64:\n\t\t\t\t\tif actual, err = v.Float(); err == nil {\n\t\t\t\t\t\tif !reflect.DeepEqual(test.expect, actual) {\n\t\t\t\t\t\t\tt.Fatal(\"expect is not equal to actual\")\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\tdefault:\n\t\t\t\t\tactual = v.Load()\n\t\t\t\t\tif !reflect.DeepEqual(test.expect, actual) {\n\t\t\t\t\t\tt.Logf(\"expect: %#v, actual: %#v\", test.expect, actual)\n\t\t\t\t\t\tt.Fail()\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif err != nil {\n\t\t\t\t\tt.Error(err)\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tt.Error(\"value path not found\")\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestNewDefaultResolver(t *testing.T) {\n\tvar (\n\t\tportString = \"8080\"\n\t\tcountInt   = 10\n\t\trateFloat  = 0.9\n\t)\n\n\tdata := map[string]any{\n\t\t\"foo\": map[string]any{\n\t\t\t\"bar\": map[string]any{\n\t\t\t\t\"notexist\": \"${NOTEXIST:100}\",\n\t\t\t\t\"port\":     \"${PORT:\\\"8081\\\"}\",\n\t\t\t\t\"count\":    \"${COUNT:\\\"0\\\"}\",\n\t\t\t\t\"enable\":   \"${ENABLE:false}\",\n\t\t\t\t\"rate\":     \"${RATE}\",\n\t\t\t\t\"empty\":    \"${EMPTY:foobar}\",\n\t\t\t\t\"url\":      \"${URL:\\\"http://example.com\\\"}\",\n\t\t\t\t\"array\": []any{\n\t\t\t\t\t\"${PORT}\",\n\t\t\t\t\tmap[string]any{\"foobar\": \"${NOTEXIST:\\\"8081\\\"}\"},\n\t\t\t\t},\n\t\t\t\t\"value1\": \"${test.value}\",\n\t\t\t\t\"value2\": \"$PORT\",\n\t\t\t\t\"value3\": \"abc${PORT}foo${COUNT}bar\",\n\t\t\t\t\"value4\": \"${foo${bar}}\",\n\t\t\t},\n\t\t},\n\t\t\"test\": map[string]any{\n\t\t\t\"value\": \"foobar\",\n\t\t},\n\t\t\"PORT\":   \"\\\"8080\\\"\",\n\t\t\"COUNT\":  \"\\\"10\\\"\",\n\t\t\"ENABLE\": \"true\",\n\t\t\"RATE\":   \"0.9\",\n\t\t\"EMPTY\":  \"\",\n\t}\n\n\ttests := []struct {\n\t\tname   string\n\t\tpath   string\n\t\texpect any\n\t}{\n\t\t{\n\t\t\tname:   \"test not exist int env with default\",\n\t\t\tpath:   \"foo.bar.notexist\",\n\t\t\texpect: 100,\n\t\t},\n\t\t{\n\t\t\tname:   \"test string with default\",\n\t\t\tpath:   \"foo.bar.port\",\n\t\t\texpect: portString,\n\t\t},\n\t\t{\n\t\t\tname:   \"test int with default\",\n\t\t\tpath:   \"foo.bar.count\",\n\t\t\texpect: countInt,\n\t\t},\n\t\t{\n\t\t\tname:   \"test bool with default\",\n\t\t\tpath:   \"foo.bar.enable\",\n\t\t\texpect: true,\n\t\t},\n\t\t{\n\t\t\tname:   \"test float without default\",\n\t\t\tpath:   \"foo.bar.rate\",\n\t\t\texpect: rateFloat,\n\t\t},\n\t\t{\n\t\t\tname:   \"test empty value with default\",\n\t\t\tpath:   \"foo.bar.empty\",\n\t\t\texpect: \"\",\n\t\t},\n\t\t{\n\t\t\tname:   \"test url with default\",\n\t\t\tpath:   \"foo.bar.url\",\n\t\t\texpect: \"http://example.com\",\n\t\t},\n\t\t{\n\t\t\tname:   \"test array\",\n\t\t\tpath:   \"foo.bar.array\",\n\t\t\texpect: []any{portString, map[string]any{\"foobar\": \"8081\"}},\n\t\t},\n\t\t{\n\t\t\tname:   \"test ${test.value}\",\n\t\t\tpath:   \"foo.bar.value1\",\n\t\t\texpect: \"foobar\",\n\t\t},\n\t\t{\n\t\t\tname:   \"test $PORT\",\n\t\t\tpath:   \"foo.bar.value2\",\n\t\t\texpect: \"$PORT\",\n\t\t},\n\t\t//{\n\t\t//\tname:   \"test abc${PORT}foo${COUNT}bar\",\n\t\t//\tpath:   \"foo.bar.value3\",\n\t\t//\texpect: \"abc8080foo10bar\",\n\t\t//},\n\t\t{\n\t\t\tname:   \"test ${foo${bar}}\",\n\t\t\tpath:   \"foo.bar.value4\",\n\t\t\texpect: \"\",\n\t\t},\n\t}\n\n\tfor _, test := range tests {\n\t\tt.Run(test.name, func(t *testing.T) {\n\t\t\tfn := newActualTypesResolver(true)\n\t\t\terr := fn(data)\n\t\t\tif err != nil {\n\t\t\t\tt.Fatal(err)\n\t\t\t}\n\t\t\trd := reader{\n\t\t\t\tvalues: data,\n\t\t\t}\n\t\t\tif v, ok := rd.Value(test.path); ok {\n\t\t\t\tvar actual any\n\t\t\t\tswitch test.expect.(type) {\n\t\t\t\tcase int:\n\t\t\t\t\tif actual, err = v.Int(); err == nil {\n\t\t\t\t\t\tif !reflect.DeepEqual(test.expect.(int), int(actual.(int64))) {\n\t\t\t\t\t\t\tt.Fatal(\"expect is not equal to actual\")\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\tcase string:\n\t\t\t\t\tif actual, err = v.String(); err == nil {\n\t\t\t\t\t\tif !reflect.DeepEqual(test.expect, actual) {\n\t\t\t\t\t\t\tt.Fatal(\"expect is not equal to actual\")\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\tcase bool:\n\t\t\t\t\tif actual, err = v.Bool(); err == nil {\n\t\t\t\t\t\tif !reflect.DeepEqual(test.expect, actual) {\n\t\t\t\t\t\t\tt.Fatal(\"expect is not equal to actual\")\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\tcase float64:\n\t\t\t\t\tif actual, err = v.Float(); err == nil {\n\t\t\t\t\t\tif !reflect.DeepEqual(test.expect, actual) {\n\t\t\t\t\t\t\tt.Fatal(\"expect is not equal to actual\")\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\tdefault:\n\t\t\t\t\tactual = v.Load()\n\t\t\t\t\tif !reflect.DeepEqual(test.expect, actual) {\n\t\t\t\t\t\tt.Logf(\"expect: %#v, actual: %#v\", test.expect, actual)\n\t\t\t\t\t\tt.Fail()\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif err != nil {\n\t\t\t\t\tt.Error(err)\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tt.Error(\"value path not found\")\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestExpand(t *testing.T) {\n\ttests := []struct {\n\t\tinput   string\n\t\tmapping func(string) string\n\t\twant    string\n\t}{\n\t\t{\n\t\t\tinput: \"${a}\",\n\t\t\tmapping: func(s string) string {\n\t\t\t\treturn strings.ToUpper(s)\n\t\t\t},\n\t\t\twant: \"A\",\n\t\t},\n\t\t{\n\t\t\tinput: \"a\",\n\t\t\tmapping: func(s string) string {\n\t\t\t\treturn strings.ToUpper(s)\n\t\t\t},\n\t\t\twant: \"a\",\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tif got := expand(tt.input, tt.mapping, false); got != tt.want {\n\t\t\tt.Errorf(\"expand() want: %s, got: %s\", tt.want, got)\n\t\t}\n\t}\n}\n\nfunc TestWithMergeFunc(t *testing.T) {\n\tc := &options{}\n\ta := func(any, any) error {\n\t\treturn nil\n\t}\n\tWithMergeFunc(a)(c)\n\tif c.merge == nil {\n\t\tt.Fatal(\"c.merge is nil\")\n\t}\n}\n"
  },
  {
    "path": "config/reader.go",
    "content": "package config\n\nimport (\n\t\"bytes\"\n\t\"encoding/gob\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"strings\"\n\t\"sync\"\n\n\t\"google.golang.org/protobuf/encoding/protojson\"\n\t\"google.golang.org/protobuf/proto\"\n\n\t\"github.com/go-kratos/kratos/v2/log\"\n)\n\n// Reader is config reader.\ntype Reader interface {\n\tMerge(...*KeyValue) error\n\tValue(string) (Value, bool)\n\tSource() ([]byte, error)\n\tResolve() error\n}\n\ntype reader struct {\n\topts   options\n\tvalues map[string]any\n\tlock   sync.Mutex\n}\n\nfunc newReader(opts options) Reader {\n\treturn &reader{\n\t\topts:   opts,\n\t\tvalues: make(map[string]any),\n\t\tlock:   sync.Mutex{},\n\t}\n}\n\nfunc (r *reader) Merge(kvs ...*KeyValue) error {\n\tmerged, err := r.cloneMap()\n\tif err != nil {\n\t\treturn err\n\t}\n\tfor _, kv := range kvs {\n\t\tnext := make(map[string]any)\n\t\tif err := r.opts.decoder(kv, next); err != nil {\n\t\t\tlog.Errorf(\"Failed to config decode error: %v key: %s value: %s\", err, kv.Key, string(kv.Value))\n\t\t\treturn err\n\t\t}\n\t\tif err := r.opts.merge(&merged, convertMap(next)); err != nil {\n\t\t\tlog.Errorf(\"Failed to config merge error: %v key: %s value: %s\", err, kv.Key, string(kv.Value))\n\t\t\treturn err\n\t\t}\n\t}\n\tr.lock.Lock()\n\tr.values = merged\n\tr.lock.Unlock()\n\treturn nil\n}\n\nfunc (r *reader) Value(path string) (Value, bool) {\n\tr.lock.Lock()\n\tdefer r.lock.Unlock()\n\treturn readValue(r.values, path)\n}\n\nfunc (r *reader) Source() ([]byte, error) {\n\tr.lock.Lock()\n\tdefer r.lock.Unlock()\n\treturn marshalJSON(convertMap(r.values))\n}\n\nfunc (r *reader) Resolve() error {\n\tr.lock.Lock()\n\tdefer r.lock.Unlock()\n\treturn r.opts.resolver(r.values)\n}\n\nfunc (r *reader) cloneMap() (map[string]any, error) {\n\tr.lock.Lock()\n\tdefer r.lock.Unlock()\n\treturn cloneMap(r.values)\n}\n\nfunc cloneMap(src map[string]any) (map[string]any, error) {\n\t// https://gist.github.com/soroushjp/0ec92102641ddfc3ad5515ca76405f4d\n\tvar buf bytes.Buffer\n\tgob.Register(map[string]any{})\n\tgob.Register([]any{})\n\tenc := gob.NewEncoder(&buf)\n\tdec := gob.NewDecoder(&buf)\n\terr := enc.Encode(src)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tvar clone map[string]any\n\terr = dec.Decode(&clone)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn clone, nil\n}\n\nfunc convertMap(src any) any {\n\tswitch m := src.(type) {\n\tcase map[string]any:\n\t\tdst := make(map[string]any, len(m))\n\t\tfor k, v := range m {\n\t\t\tdst[k] = convertMap(v)\n\t\t}\n\t\treturn dst\n\tcase map[any]any:\n\t\tdst := make(map[string]any, len(m))\n\t\tfor k, v := range m {\n\t\t\tdst[fmt.Sprint(k)] = convertMap(v)\n\t\t}\n\t\treturn dst\n\tcase []any:\n\t\tdst := make([]any, len(m))\n\t\tfor k, v := range m {\n\t\t\tdst[k] = convertMap(v)\n\t\t}\n\t\treturn dst\n\tcase []byte:\n\t\t// there will be no binary data in the config data\n\t\treturn string(m)\n\tdefault:\n\t\treturn src\n\t}\n}\n\n// readValue read Value in given map[string]interface{}\n// by the given path, will return false if not found.\nfunc readValue(values map[string]any, path string) (Value, bool) {\n\tvar (\n\t\tnext = values\n\t\tkeys = strings.Split(path, \".\")\n\t\tlast = len(keys) - 1\n\t)\n\tfor idx, key := range keys {\n\t\tvalue, ok := next[key]\n\t\tif !ok {\n\t\t\treturn nil, false\n\t\t}\n\t\tif idx == last {\n\t\t\tav := &atomicValue{}\n\t\t\tav.Store(value)\n\t\t\treturn av, true\n\t\t}\n\t\tswitch vm := value.(type) {\n\t\tcase map[string]any:\n\t\t\tnext = vm\n\t\tdefault:\n\t\t\treturn nil, false\n\t\t}\n\t}\n\treturn nil, false\n}\n\nfunc marshalJSON(v any) ([]byte, error) {\n\tif m, ok := v.(proto.Message); ok {\n\t\treturn protojson.MarshalOptions{EmitUnpopulated: true}.Marshal(m)\n\t}\n\treturn json.Marshal(v)\n}\n\nfunc unmarshalJSON(data []byte, v any) error {\n\tif m, ok := v.(proto.Message); ok {\n\t\treturn protojson.UnmarshalOptions{DiscardUnknown: true}.Unmarshal(data, m)\n\t}\n\treturn json.Unmarshal(data, v)\n}\n"
  },
  {
    "path": "config/reader_test.go",
    "content": "package config\n\nimport (\n\t\"fmt\"\n\t\"reflect\"\n\t\"testing\"\n\n\t\"github.com/go-kratos/kratos/v2/encoding\"\n\n\t\"dario.cat/mergo\"\n)\n\nfunc TestReader_Merge(t *testing.T) {\n\tvar (\n\t\terr error\n\t\tok  bool\n\t)\n\topts := options{\n\t\tdecoder: func(kv *KeyValue, v map[string]any) error {\n\t\t\tif codec := encoding.GetCodec(kv.Format); codec != nil {\n\t\t\t\treturn codec.Unmarshal(kv.Value, &v)\n\t\t\t}\n\t\t\treturn fmt.Errorf(\"unsupported key: %s format: %s\", kv.Key, kv.Format)\n\t\t},\n\t\tresolver: defaultResolver,\n\t\tmerge: func(dst, src any) error {\n\t\t\treturn mergo.Map(dst, src, mergo.WithOverride)\n\t\t},\n\t}\n\tr := newReader(opts)\n\terr = r.Merge(&KeyValue{\n\t\tKey:    \"a\",\n\t\tValue:  []byte(\"bad\"),\n\t\tFormat: \"json\",\n\t})\n\tif err == nil {\n\t\tt.Fatal(\"err is nil\")\n\t}\n\n\terr = r.Merge(&KeyValue{\n\t\tKey:    \"b\",\n\t\tValue:  []byte(`{\"nice\": \"boat\", \"x\": 1}`),\n\t\tFormat: \"json\",\n\t})\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tvv, ok := r.Value(\"nice\")\n\tif !ok {\n\t\tt.Fatal(\"ok is false\")\n\t}\n\tvvv, err := vv.String()\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif vvv != \"boat\" {\n\t\tt.Fatal(`vvv is not equal to \"boat\"`)\n\t}\n\n\terr = r.Merge(&KeyValue{\n\t\tKey:    \"b\",\n\t\tValue:  []byte(`{\"x\": 2}`),\n\t\tFormat: \"json\",\n\t})\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tvv, ok = r.Value(\"x\")\n\tif !ok {\n\t\tt.Fatal(\"ok is false\")\n\t}\n\tvvx, err := vv.Int()\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif vvx != 2 {\n\t\tt.Fatal(\"vvx is not equal to 2\")\n\t}\n}\n\nfunc TestReader_Value(t *testing.T) {\n\topts := options{\n\t\tdecoder: func(kv *KeyValue, v map[string]any) error {\n\t\t\tif codec := encoding.GetCodec(kv.Format); codec != nil {\n\t\t\t\treturn codec.Unmarshal(kv.Value, &v)\n\t\t\t}\n\t\t\treturn fmt.Errorf(\"unsupported key: %s format: %s\", kv.Key, kv.Format)\n\t\t},\n\t\tresolver: defaultResolver,\n\t\tmerge: func(dst, src any) error {\n\t\t\treturn mergo.Map(dst, src, mergo.WithOverride)\n\t\t},\n\t}\n\n\tymlval := `\na:\n  b:\n    X: 1\n    Y: \"lol\"\n    z: true\n`\n\ttests := []struct {\n\t\tname string\n\t\tkv   KeyValue\n\t}{\n\t\t{\n\t\t\tname: \"json value\",\n\t\t\tkv: KeyValue{\n\t\t\t\tKey:    \"config\",\n\t\t\t\tValue:  []byte(`{\"a\": {\"b\": {\"X\": 1, \"Y\": \"lol\", \"z\": true}}}`),\n\t\t\t\tFormat: \"json\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"yaml value\",\n\t\t\tkv: KeyValue{\n\t\t\t\tKey:    \"config\",\n\t\t\t\tValue:  []byte(ymlval),\n\t\t\t\tFormat: \"yaml\",\n\t\t\t},\n\t\t},\n\t}\n\n\tfor _, test := range tests {\n\t\tt.Run(test.name, func(t *testing.T) {\n\t\t\tr := newReader(opts)\n\t\t\terr := r.Merge(&test.kv)\n\t\t\tif err != nil {\n\t\t\t\tt.Fatal(err)\n\t\t\t}\n\t\t\tvv, ok := r.Value(\"a.b.X\")\n\t\t\tif !ok {\n\t\t\t\tt.Fatal(\"ok is false\")\n\t\t\t}\n\t\t\tvvv, err := vv.Int()\n\t\t\tif err != nil {\n\t\t\t\tt.Fatal(err)\n\t\t\t}\n\t\t\tif int64(1) != vvv {\n\t\t\t\tt.Fatal(\"vvv is not equal to 1\")\n\t\t\t}\n\n\t\t\tvv, ok = r.Value(\"a.b.Y\")\n\t\t\tif !ok {\n\t\t\t\tt.Fatal(\"ok is false\")\n\t\t\t}\n\t\t\tvvy, err := vv.String()\n\t\t\tif err != nil {\n\t\t\t\tt.Fatal(err)\n\t\t\t}\n\t\t\tif vvy != \"lol\" {\n\t\t\t\tt.Fatal(`vvy is not equal to \"lol\"`)\n\t\t\t}\n\n\t\t\tvv, ok = r.Value(\"a.b.z\")\n\t\t\tif !ok {\n\t\t\t\tt.Fatal(\"ok is false\")\n\t\t\t}\n\t\t\tvvz, err := vv.Bool()\n\t\t\tif err != nil {\n\t\t\t\tt.Fatal(err)\n\t\t\t}\n\t\t\tif !vvz {\n\t\t\t\tt.Fatal(\"vvz is not equal to true\")\n\t\t\t}\n\n\t\t\t_, ok = r.Value(\"aasasdg=234l.asdfk,\")\n\t\t\tif ok {\n\t\t\t\tt.Fatal(\"ok is true\")\n\t\t\t}\n\n\t\t\t_, ok = r.Value(\"aas......asdg=234l.asdfk,\")\n\t\t\tif ok {\n\t\t\t\tt.Fatal(\"ok is true\")\n\t\t\t}\n\n\t\t\t_, ok = r.Value(\"a.b.Y.\")\n\t\t\tif ok {\n\t\t\t\tt.Fatal(\"ok is true\")\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestReader_Source(t *testing.T) {\n\tvar err error\n\topts := options{\n\t\tdecoder: func(kv *KeyValue, v map[string]any) error {\n\t\t\tif codec := encoding.GetCodec(kv.Format); codec != nil {\n\t\t\t\treturn codec.Unmarshal(kv.Value, &v)\n\t\t\t}\n\t\t\treturn fmt.Errorf(\"unsupported key: %s format: %s\", kv.Key, kv.Format)\n\t\t},\n\t\tresolver: defaultResolver,\n\t\tmerge: func(dst, src any) error {\n\t\t\treturn mergo.Map(dst, src, mergo.WithOverride)\n\t\t},\n\t}\n\tr := newReader(opts)\n\terr = r.Merge(&KeyValue{\n\t\tKey:    \"b\",\n\t\tValue:  []byte(`{\"a\": {\"b\": {\"X\": 1}}}`),\n\t\tFormat: \"json\",\n\t})\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tb, err := r.Source()\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif !reflect.DeepEqual([]byte(`{\"a\":{\"b\":{\"X\":1}}}`), b) {\n\t\tt.Fatal(\"[]byte(`{\\\"a\\\":{\\\"b\\\":{\\\"X\\\":1}}}`) is not equal to b\")\n\t}\n}\n\nfunc TestCloneMap(t *testing.T) {\n\ttests := []struct {\n\t\tinput map[string]any\n\t\twant  map[string]any\n\t}{\n\t\t{\n\t\t\tinput: map[string]any{\n\t\t\t\t\"a\": 1,\n\t\t\t\t\"b\": \"2\",\n\t\t\t\t\"c\": true,\n\t\t\t},\n\t\t\twant: map[string]any{\n\t\t\t\t\"a\": 1,\n\t\t\t\t\"b\": \"2\",\n\t\t\t\t\"c\": true,\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tinput: map[string]any{},\n\t\t\twant:  map[string]any{},\n\t\t},\n\t\t{\n\t\t\tinput: nil,\n\t\t\twant:  map[string]any{},\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tif got, err := cloneMap(tt.input); err != nil {\n\t\t\tt.Errorf(\"expect no err, got %v\", err)\n\t\t} else if !reflect.DeepEqual(got, tt.want) {\n\t\t\tt.Errorf(\"cloneMap(%v) = %v, want %v\", tt.input, got, tt.want)\n\t\t}\n\t}\n}\n\nfunc TestConvertMap(t *testing.T) {\n\ttests := []struct {\n\t\tinput any\n\t\twant  any\n\t}{\n\t\t{\n\t\t\tinput: map[string]any{\n\t\t\t\t\"a\": 1,\n\t\t\t\t\"b\": \"2\",\n\t\t\t\t\"c\": true,\n\t\t\t\t\"d\": []byte{65, 66, 67},\n\t\t\t},\n\t\t\twant: map[string]any{\n\t\t\t\t\"a\": 1,\n\t\t\t\t\"b\": \"2\",\n\t\t\t\t\"c\": true,\n\t\t\t\t\"d\": \"ABC\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tinput: []any{1, 2.0, \"3\", true, nil, []any{1, 2.0, \"3\", true, nil}},\n\t\t\twant:  []any{1, 2.0, \"3\", true, nil, []any{1, 2.0, \"3\", true, nil}},\n\t\t},\n\t\t{\n\t\t\tinput: []byte{65, 66, 67},\n\t\t\twant:  \"ABC\",\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tif got := convertMap(tt.input); !reflect.DeepEqual(got, tt.want) {\n\t\t\tt.Errorf(\"convertMap(%v) = %v, want %v\", tt.input, got, tt.want)\n\t\t}\n\t}\n}\n\nfunc TestReadValue(t *testing.T) {\n\tm := map[string]any{\n\t\t\"a\": 1,\n\t\t\"b\": map[string]any{\n\t\t\t\"c\": \"3\",\n\t\t\t\"d\": map[string]any{\n\t\t\t\t\"e\": true,\n\t\t\t},\n\t\t},\n\t}\n\tva := atomicValue{}\n\tva.Store(1)\n\n\tvbc := atomicValue{}\n\tvbc.Store(\"3\")\n\n\tvbde := atomicValue{}\n\tvbde.Store(true)\n\n\ttests := []struct {\n\t\tpath string\n\t\twant atomicValue\n\t}{\n\t\t{\n\t\t\tpath: \"a\",\n\t\t\twant: va,\n\t\t},\n\t\t{\n\t\t\tpath: \"b.c\",\n\t\t\twant: vbc,\n\t\t},\n\t\t{\n\t\t\tpath: \"b.d.e\",\n\t\t\twant: vbde,\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tif got, found := readValue(m, tt.path); !found {\n\t\t\tt.Errorf(\"expect found %v in %v, but not.\", tt.path, m)\n\t\t} else if got.Load() != tt.want.Load() {\n\t\t\tt.Errorf(\"readValue(%v, %v) = %v, want %v\", m, tt.path, got, tt.want)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "config/source.go",
    "content": "package config\n\n// KeyValue is config key value.\ntype KeyValue struct {\n\tKey    string\n\tValue  []byte\n\tFormat string\n}\n\n// Source is config source.\ntype Source interface {\n\tLoad() ([]*KeyValue, error)\n\tWatch() (Watcher, error)\n}\n\n// Watcher watches a source for changes.\ntype Watcher interface {\n\tNext() ([]*KeyValue, error)\n\tStop() error\n}\n"
  },
  {
    "path": "config/value.go",
    "content": "package config\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"reflect\"\n\t\"strconv\"\n\t\"sync/atomic\"\n\t\"time\"\n\n\t\"google.golang.org/protobuf/proto\"\n\n\tkratosjson \"github.com/go-kratos/kratos/v2/encoding/json\"\n)\n\nvar (\n\t_ Value = (*atomicValue)(nil)\n\t_ Value = (*errValue)(nil)\n)\n\n// Value is config value interface.\ntype Value interface {\n\tBool() (bool, error)\n\tInt() (int64, error)\n\tFloat() (float64, error)\n\tString() (string, error)\n\tDuration() (time.Duration, error)\n\tSlice() ([]Value, error)\n\tMap() (map[string]Value, error)\n\tScan(any) error\n\tLoad() any\n\tStore(any)\n}\n\ntype atomicValue struct {\n\tatomic.Value\n}\n\nfunc (v *atomicValue) typeAssertError() error {\n\treturn fmt.Errorf(\"type assert to %v failed\", reflect.TypeOf(v.Load()))\n}\n\nfunc (v *atomicValue) Bool() (bool, error) {\n\tswitch val := v.Load().(type) {\n\tcase bool:\n\t\treturn val, nil\n\tcase int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, float32, float64:\n\t\treturn strconv.ParseBool(fmt.Sprint(val))\n\tcase string:\n\t\treturn strconv.ParseBool(val)\n\t}\n\treturn false, v.typeAssertError()\n}\n\nfunc (v *atomicValue) Int() (int64, error) {\n\tswitch val := v.Load().(type) {\n\tcase int:\n\t\treturn int64(val), nil\n\tcase int8:\n\t\treturn int64(val), nil\n\tcase int16:\n\t\treturn int64(val), nil\n\tcase int32:\n\t\treturn int64(val), nil\n\tcase int64:\n\t\treturn val, nil\n\tcase uint:\n\t\treturn int64(val), nil\n\tcase uint8:\n\t\treturn int64(val), nil\n\tcase uint16:\n\t\treturn int64(val), nil\n\tcase uint32:\n\t\treturn int64(val), nil\n\tcase uint64:\n\t\treturn int64(val), nil\n\tcase float32:\n\t\treturn int64(val), nil\n\tcase float64:\n\t\treturn int64(val), nil\n\tcase string:\n\t\treturn strconv.ParseInt(val, 10, 64)\n\t}\n\treturn 0, v.typeAssertError()\n}\n\nfunc (v *atomicValue) Slice() ([]Value, error) {\n\tvals, ok := v.Load().([]any)\n\tif !ok {\n\t\treturn nil, v.typeAssertError()\n\t}\n\tslices := make([]Value, 0, len(vals))\n\tfor _, val := range vals {\n\t\ta := new(atomicValue)\n\t\ta.Store(val)\n\t\tslices = append(slices, a)\n\t}\n\treturn slices, nil\n}\n\nfunc (v *atomicValue) Map() (map[string]Value, error) {\n\tvals, ok := v.Load().(map[string]any)\n\tif !ok {\n\t\treturn nil, v.typeAssertError()\n\t}\n\tm := make(map[string]Value, len(vals))\n\tfor key, val := range vals {\n\t\ta := new(atomicValue)\n\t\ta.Store(val)\n\t\tm[key] = a\n\t}\n\treturn m, nil\n}\n\nfunc (v *atomicValue) Float() (float64, error) {\n\tswitch val := v.Load().(type) {\n\tcase int:\n\t\treturn float64(val), nil\n\tcase int8:\n\t\treturn float64(val), nil\n\tcase int16:\n\t\treturn float64(val), nil\n\tcase int32:\n\t\treturn float64(val), nil\n\tcase int64:\n\t\treturn float64(val), nil\n\tcase uint:\n\t\treturn float64(val), nil\n\tcase uint8:\n\t\treturn float64(val), nil\n\tcase uint16:\n\t\treturn float64(val), nil\n\tcase uint32:\n\t\treturn float64(val), nil\n\tcase uint64:\n\t\treturn float64(val), nil\n\tcase float32:\n\t\treturn float64(val), nil\n\tcase float64:\n\t\treturn val, nil\n\tcase string:\n\t\treturn strconv.ParseFloat(val, 64)\n\t}\n\treturn 0.0, v.typeAssertError()\n}\n\nfunc (v *atomicValue) String() (string, error) {\n\tswitch val := v.Load().(type) {\n\tcase string:\n\t\treturn val, nil\n\tcase bool, int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, float32, float64:\n\t\treturn fmt.Sprint(val), nil\n\tcase []byte:\n\t\treturn string(val), nil\n\tcase fmt.Stringer:\n\t\treturn val.String(), nil\n\t}\n\treturn \"\", v.typeAssertError()\n}\n\nfunc (v *atomicValue) Duration() (time.Duration, error) {\n\tval, err := v.Int()\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\treturn time.Duration(val), nil\n}\n\nfunc (v *atomicValue) Scan(obj any) error {\n\tdata, err := json.Marshal(v.Load())\n\tif err != nil {\n\t\treturn err\n\t}\n\tif pb, ok := obj.(proto.Message); ok {\n\t\treturn kratosjson.UnmarshalOptions.Unmarshal(data, pb)\n\t}\n\treturn json.Unmarshal(data, obj)\n}\n\ntype errValue struct {\n\terr error\n}\n\nfunc (v errValue) Bool() (bool, error)              { return false, v.err }\nfunc (v errValue) Int() (int64, error)              { return 0, v.err }\nfunc (v errValue) Float() (float64, error)          { return 0.0, v.err }\nfunc (v errValue) Duration() (time.Duration, error) { return 0, v.err }\nfunc (v errValue) String() (string, error)          { return \"\", v.err }\nfunc (v errValue) Scan(any) error                   { return v.err }\nfunc (v errValue) Load() any                        { return nil }\nfunc (v errValue) Store(any)                        {}\nfunc (v errValue) Slice() ([]Value, error)          { return nil, v.err }\nfunc (v errValue) Map() (map[string]Value, error)   { return nil, v.err }\n"
  },
  {
    "path": "config/value_test.go",
    "content": "package config\n\nimport (\n\t\"fmt\"\n\t\"testing\"\n\t\"time\"\n)\n\nfunc TestAtomicValue_Bool(t *testing.T) {\n\tvlist := []any{\"1\", \"t\", \"T\", \"true\", \"TRUE\", \"True\", true, 1, int32(1)}\n\tfor _, x := range vlist {\n\t\tv := atomicValue{}\n\t\tv.Store(x)\n\t\tb, err := v.Bool()\n\t\tif err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\t\tif !b {\n\t\t\tt.Fatal(\"b is not equal to true\")\n\t\t}\n\t}\n\n\tvlist = []any{\"0\", \"f\", \"F\", \"false\", \"FALSE\", \"False\", false, 0, int32(0)}\n\tfor _, x := range vlist {\n\t\tv := atomicValue{}\n\t\tv.Store(x)\n\t\tb, err := v.Bool()\n\t\tif err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\t\tif b {\n\t\t\tt.Fatal(\"b is not equal to false\")\n\t\t}\n\t}\n\n\tvlist = []any{\"bbb\", \"-1\"}\n\tfor _, x := range vlist {\n\t\tv := atomicValue{}\n\t\tv.Store(x)\n\t\t_, err := v.Bool()\n\t\tif err == nil {\n\t\t\tt.Fatal(\"err is nil\")\n\t\t}\n\t}\n}\n\nfunc TestAtomicValue_Int(t *testing.T) {\n\tvlist := []any{\"123123\", float64(123123), int64(123123), int32(123123), 123123}\n\tfor _, x := range vlist {\n\t\tv := atomicValue{}\n\t\tv.Store(x)\n\t\tb, err := v.Int()\n\t\tif err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\t\tif b != 123123 {\n\t\t\tt.Fatal(\"b is not equal to 123123\")\n\t\t}\n\t}\n\n\tvlist = []any{\"bbb\", \"-x1\", true}\n\tfor _, x := range vlist {\n\t\tv := atomicValue{}\n\t\tv.Store(x)\n\t\t_, err := v.Int()\n\t\tif err == nil {\n\t\t\tt.Fatal(\"err is nil\")\n\t\t}\n\t}\n}\n\nfunc TestAtomicValue_Float(t *testing.T) {\n\tvlist := []any{\"123123.1\", 123123.1}\n\tfor _, x := range vlist {\n\t\tv := atomicValue{}\n\t\tv.Store(x)\n\t\tb, err := v.Float()\n\t\tif err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\t\tif b != 123123.1 {\n\t\t\tt.Fatal(\"b is not equal to 123123.1\")\n\t\t}\n\t}\n\n\tvlist = []any{\"bbb\", \"-x1\"}\n\tfor _, x := range vlist {\n\t\tv := atomicValue{}\n\t\tv.Store(x)\n\t\t_, err := v.Float()\n\t\tif err == nil {\n\t\t\tt.Fatal(\"err is nil\")\n\t\t}\n\t}\n}\n\ntype ts struct {\n\tName string\n\tAge  int\n}\n\nfunc (t ts) String() string {\n\treturn fmt.Sprintf(\"%s%d\", t.Name, t.Age)\n}\n\nfunc TestAtomicValue_String(t *testing.T) {\n\tvlist := []any{\"1\", float64(1), int64(1), 1, int64(1)}\n\tfor _, x := range vlist {\n\t\tv := atomicValue{}\n\t\tv.Store(x)\n\t\tb, err := v.String()\n\t\tif err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\t\tif b != \"1\" {\n\t\t\tt.Fatal(\"b is not equal to 1\")\n\t\t}\n\t}\n\n\tv := atomicValue{}\n\tv.Store(true)\n\tb, err := v.String()\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif b != \"true\" {\n\t\tt.Fatal(`b is not equal to \"true\"`)\n\t}\n\n\tv = atomicValue{}\n\tv.Store(ts{\n\t\tName: \"test\",\n\t\tAge:  10,\n\t})\n\tb, err = v.String()\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif b != \"test10\" {\n\t\tt.Fatal(`b is not equal to \"test10\"`)\n\t}\n}\n\nfunc TestAtomicValue_Duration(t *testing.T) {\n\tvlist := []any{int64(5)}\n\tfor _, x := range vlist {\n\t\tv := atomicValue{}\n\t\tv.Store(x)\n\t\tb, err := v.Duration()\n\t\tif err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\t\tif b != time.Duration(5) {\n\t\t\tt.Fatal(\"b is not equal to time.Duration(5)\")\n\t\t}\n\t}\n}\n\nfunc TestAtomicValue_Slice(t *testing.T) {\n\tvlist := []any{int64(5)}\n\tv := atomicValue{}\n\tv.Store(vlist)\n\tslices, err := v.Slice()\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tfor _, v := range slices {\n\t\tb, err := v.Duration()\n\t\tif err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\t\tif b != time.Duration(5) {\n\t\t\tt.Fatal(\"b is not equal to time.Duration(5)\")\n\t\t}\n\t}\n}\n\nfunc TestAtomicValue_Map(t *testing.T) {\n\tvlist := make(map[string]any)\n\tvlist[\"5\"] = int64(5)\n\tvlist[\"text\"] = \"text\"\n\tv := atomicValue{}\n\tv.Store(vlist)\n\tm, err := v.Map()\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tfor k, v := range m {\n\t\tif k == \"5\" {\n\t\t\tb, err := v.Duration()\n\t\t\tif err != nil {\n\t\t\t\tt.Fatal(err)\n\t\t\t}\n\t\t\tif b != time.Duration(5) {\n\t\t\t\tt.Fatal(\"b is not equal to time.Duration(5)\")\n\t\t\t}\n\t\t} else {\n\t\t\tb, err := v.String()\n\t\t\tif err != nil {\n\t\t\t\tt.Fatal(err)\n\t\t\t}\n\t\t\tif b != \"text\" {\n\t\t\t\tt.Fatal(`b is not equal to \"text\"`)\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc TestAtomicValue_Scan(t *testing.T) {\n\tv := atomicValue{}\n\terr := v.Scan(&struct {\n\t\tA string `json:\"a\"`\n\t}{\"a\"})\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\terr = v.Scan(&struct {\n\t\tA string `json:\"a\"`\n\t}{\"a\"})\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n}\n"
  },
  {
    "path": "contrib/config/apollo/README.md",
    "content": "## Apollo config center\n\nThis module implements the `config.Source` interface in kratos based apollo config management center.\n\n[![go.dev reference](https://img.shields.io/badge/go.dev-reference-007d9c?logo=go&logoColor=white&style=flat-square)](https://pkg.go.dev/github.com/go-kratos/kratos/contrib/config/apollo/v2)\n\n### Quick start\n\n```go\nimport (\n\t\"fmt\"\n\t\"log\"\n\n\t\"github.com/go-kratos/kratos/contrib/config/apollo/v2\"\n\t\"github.com/go-kratos/kratos/v2/config\"\n)\n\nfunc main() {\n\tc := config.New(\n\t\tconfig.WithSource(\n\t\t\tapollo.NewSource(\n\t\t\t\tapollo.WithAppID(\"kratos\"),\n\t\t\t\tapollo.WithCluster(\"dev\"),\n\t\t\t\tapollo.WithEndpoint(\"http://localhost:8080\"),\n\t\t\t\tapollo.WithNamespace(\"application,event.yaml,demo.json\"),\n\t\t\t\tapollo.WithEnableBackup(),\n\t\t\t\tapollo.WithSecret(\"ad75b33c77ae4b9c9626d969c44f41ee\"),\n\t\t\t),\n\t\t),\n\t)\n\tvar bc bootstrap\n\tif err := c.Load(); err != nil {\n\t\tpanic(err)\n\t}\n\n\t// use value and watch operations，help yourself.\n}\n```\n\n### Options list\n\n> You get what you see.\n\n```go\n// specify the app id\nfunc WithAppID(appID string) Option\n// specify the cluster of application\nfunc WithCluster(cluster string) Option\n\n// enable backup or not, and where to back up them.\nfunc WithBackupPath(backupPath string) Option\nfunc WithDisableBackup() Option\nfunc WithEnableBackup() Option\n\n// specify apollo endpoint, such as http://localhost:8080\nfunc WithEndpoint(endpoint string) Option\n\n// namespaces to load, comma to separate.\nfunc WithNamespace(name string) Option\n\n// secret is the apollo secret key to access application config.\nfunc WithSecret(secret string) Option\n```\n\n### Notice\n\napollo config center use `Namespace` to be part of the key. For example:\n\n***application.json***\n\n```json\n{\n  \"http\": {\n    \"address\": \":8080\",\n    \"tls\": {\n      \"enable\": false,\n      \"cert_file\": \"\",\n      \"key_file\": \"\"\n    }\n  }\n}\n```\n\nyou got them in kratos config instance maybe look like:\n\n```go\nconfig := map[string]interface{}{\n\t// application be part of the key path.\n\t\"application\": map[string]interface{}{\n\t\t\"http\": map[string]interface{}{\n\t\t\t\"address\": \":8080\",\n\t\t\t\"tls\": map[string]interface{}{\n\t\t\t\t\"enable\": false,\n\t\t\t\t\"cert_file\": \"\",\n\t\t\t\t\"key_file\": \"\",\n\t\t\t},\n\t\t},\n\t},\n}\n```\n"
  },
  {
    "path": "contrib/config/apollo/apollo.go",
    "content": "package apollo\n\nimport (\n\t\"strings\"\n\n\t\"github.com/apolloconfig/agollo/v4\"\n\t\"github.com/apolloconfig/agollo/v4/constant\"\n\tapolloconfig \"github.com/apolloconfig/agollo/v4/env/config\"\n\t\"github.com/apolloconfig/agollo/v4/extension\"\n\n\t\"github.com/go-kratos/kratos/v2/config\"\n\t\"github.com/go-kratos/kratos/v2/encoding\"\n\t\"github.com/go-kratos/kratos/v2/log\"\n)\n\ntype apollo struct {\n\tclient agollo.Client\n\topt    *options\n}\n\nconst (\n\tyaml       = \"yaml\"\n\tyml        = \"yml\"\n\tjson       = \"json\"\n\tproperties = \"properties\"\n)\n\nvar formats map[string]struct{}\n\n// Option is apollo option\ntype Option func(*options)\n\ntype options struct {\n\tappid          string\n\tsecret         string\n\tcluster        string\n\tendpoint       string\n\tnamespace      string\n\tisBackupConfig bool\n\tbackupPath     string\n\toriginConfig   bool\n}\n\n// WithAppID with apollo config app id\nfunc WithAppID(appID string) Option {\n\treturn func(o *options) {\n\t\to.appid = appID\n\t}\n}\n\n// WithCluster with apollo config cluster\nfunc WithCluster(cluster string) Option {\n\treturn func(o *options) {\n\t\to.cluster = cluster\n\t}\n}\n\n// WithEndpoint with apollo config conf server ip\nfunc WithEndpoint(endpoint string) Option {\n\treturn func(o *options) {\n\t\to.endpoint = endpoint\n\t}\n}\n\n// WithEnableBackup with apollo config enable backup config\nfunc WithEnableBackup() Option {\n\treturn func(o *options) {\n\t\to.isBackupConfig = true\n\t}\n}\n\n// WithDisableBackup with apollo config enable backup config\nfunc WithDisableBackup() Option {\n\treturn func(o *options) {\n\t\to.isBackupConfig = false\n\t}\n}\n\n// WithSecret with apollo config app secret\nfunc WithSecret(secret string) Option {\n\treturn func(o *options) {\n\t\to.secret = secret\n\t}\n}\n\n// WithNamespace with apollo config namespace name\nfunc WithNamespace(name string) Option {\n\treturn func(o *options) {\n\t\to.namespace = name\n\t}\n}\n\n// WithBackupPath with apollo config backupPath\nfunc WithBackupPath(backupPath string) Option {\n\treturn func(o *options) {\n\t\to.backupPath = backupPath\n\t}\n}\n\n// WithOriginalConfig use the original configuration file without parse processing\nfunc WithOriginalConfig() Option {\n\treturn func(o *options) {\n\t\textension.AddFormatParser(constant.JSON, &jsonExtParser{})\n\t\textension.AddFormatParser(constant.YAML, &yamlExtParser{})\n\t\textension.AddFormatParser(constant.YML, &yamlExtParser{})\n\t\to.originConfig = true\n\t}\n}\n\nfunc NewSource(opts ...Option) config.Source {\n\top := options{}\n\tfor _, o := range opts {\n\t\to(&op)\n\t}\n\tclient, err := agollo.StartWithConfig(func() (*apolloconfig.AppConfig, error) {\n\t\treturn &apolloconfig.AppConfig{\n\t\t\tAppID:            op.appid,\n\t\t\tCluster:          op.cluster,\n\t\t\tNamespaceName:    op.namespace,\n\t\t\tIP:               op.endpoint,\n\t\t\tIsBackupConfig:   op.isBackupConfig,\n\t\t\tSecret:           op.secret,\n\t\t\tBackupConfigPath: op.backupPath,\n\t\t}, nil\n\t})\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\treturn &apollo{client: client, opt: &op}\n}\n\nfunc format(ns string) string {\n\tarr := strings.Split(ns, \".\")\n\tsuffix := arr[len(arr)-1]\n\tif len(arr) <= 1 || suffix == properties {\n\t\treturn json\n\t}\n\tif _, ok := formats[suffix]; !ok {\n\t\t// fallback\n\t\treturn json\n\t}\n\n\treturn suffix\n}\n\nfunc (e *apollo) load() []*config.KeyValue {\n\tkvs := make([]*config.KeyValue, 0)\n\tnamespaces := strings.Split(e.opt.namespace, \",\")\n\n\tfor _, ns := range namespaces {\n\t\tif !e.opt.originConfig {\n\t\t\tkv, err := e.getConfig(ns)\n\t\t\tif err != nil {\n\t\t\t\tlog.Errorf(\"apollo get config failed，err:%v\", err)\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tkvs = append(kvs, kv)\n\t\t\tcontinue\n\t\t}\n\t\tif strings.Contains(ns, \".\") && !strings.HasSuffix(ns, \".\"+properties) &&\n\t\t\t(format(ns) == yaml || format(ns) == yml || format(ns) == json) {\n\t\t\tkv, err := e.getOriginConfig(ns)\n\t\t\tif err != nil {\n\t\t\t\tlog.Errorf(\"apollo get config failed，err:%v\", err)\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tkvs = append(kvs, kv)\n\t\t\tcontinue\n\t\t}\n\t\tkv, err := e.getConfig(ns)\n\t\tif err != nil {\n\t\t\tlog.Errorf(\"apollo get config failed，err:%v\", err)\n\t\t\tcontinue\n\t\t}\n\t\tkvs = append(kvs, kv)\n\t}\n\treturn kvs\n}\n\nfunc (e *apollo) getConfig(ns string) (*config.KeyValue, error) {\n\tnext := map[string]any{}\n\te.client.GetConfigCache(ns).Range(func(key, value any) bool {\n\t\t// all values are out properties format\n\t\tresolve(genKey(ns, key.(string)), value, next)\n\t\treturn true\n\t})\n\tf := format(ns)\n\tcodec := encoding.GetCodec(f)\n\tval, err := codec.Marshal(next)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn &config.KeyValue{\n\t\tKey:    ns,\n\t\tValue:  val,\n\t\tFormat: f,\n\t}, nil\n}\n\nfunc (e apollo) getOriginConfig(ns string) (*config.KeyValue, error) {\n\tvalue, err := e.client.GetConfigCache(ns).Get(\"content\")\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\t// serialize the namespace content KeyValue into bytes.\n\treturn &config.KeyValue{\n\t\tKey:    ns,\n\t\tValue:  []byte(value.(string)),\n\t\tFormat: format(ns),\n\t}, nil\n}\n\nfunc (e *apollo) Load() (kv []*config.KeyValue, err error) {\n\treturn e.load(), nil\n}\n\nfunc (e *apollo) Watch() (config.Watcher, error) {\n\tw, err := newWatcher(e)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn w, nil\n}\n\n// resolve convert kv pair into one map[string]interface{} by split key into different\n// map level. such as: app.name = \"application\" => map[app][name] = \"application\"\nfunc resolve(key string, value any, target map[string]any) {\n\t// expand key \"aaa.bbb\" into map[aaa]map[bbb]interface{}\n\tkeys := strings.Split(key, \".\")\n\tlast := len(keys) - 1\n\tcursor := target\n\n\tfor i, k := range keys {\n\t\tif i == last {\n\t\t\tcursor[k] = value\n\t\t\tbreak\n\t\t}\n\n\t\t// not the last key, be deeper\n\t\tv, ok := cursor[k]\n\t\tif !ok {\n\t\t\t// create a new map\n\t\t\tdeeper := make(map[string]any)\n\t\t\tcursor[k] = deeper\n\t\t\tcursor = deeper\n\t\t\tcontinue\n\t\t}\n\n\t\t// current exists, then check existing value type, if it's not map\n\t\t// that means duplicate keys, and at least one is not map instance.\n\t\tif cursor, ok = v.(map[string]any); !ok {\n\t\t\tlog.Warnf(\"duplicate key: %v\\n\", strings.Join(keys[:i+1], \".\"))\n\t\t\tbreak\n\t\t}\n\t}\n}\n\n// genKey got the key of config.KeyValue pair.\n// eg: namespace.ext with subKey got namespace.subKey\nfunc genKey(ns, sub string) string {\n\tarr := strings.Split(ns, \".\")\n\tif len(arr) == 1 {\n\t\tif ns == \"\" {\n\t\t\treturn sub\n\t\t}\n\n\t\treturn ns + \".\" + sub\n\t}\n\n\tsuffix := arr[len(arr)-1]\n\t_, ok := formats[suffix]\n\tif ok {\n\t\treturn strings.Join(arr[:len(arr)-1], \".\") + \".\" + sub\n\t}\n\n\treturn ns + \".\" + sub\n}\n\nfunc init() {\n\tformats = make(map[string]struct{})\n\n\tformats[yaml] = struct{}{}\n\tformats[yml] = struct{}{}\n\tformats[json] = struct{}{}\n\tformats[properties] = struct{}{}\n}\n"
  },
  {
    "path": "contrib/config/apollo/apollo_test.go",
    "content": "package apollo\n\nimport (\n\t\"testing\"\n)\n\nfunc Test_genKey(t *testing.T) {\n\ttype args struct {\n\t\tns  string\n\t\tsub string\n\t}\n\ttests := []struct {\n\t\tname string\n\t\targs args\n\t\twant string\n\t}{\n\t\t{\n\t\t\tname: \"blank namespace\",\n\t\t\targs: args{\n\t\t\t\tns:  \"\",\n\t\t\t\tsub: \"x.y\",\n\t\t\t},\n\t\t\twant: \"x.y\",\n\t\t},\n\t\t{\n\t\t\tname: \"properties namespace\",\n\t\t\targs: args{\n\t\t\t\tns:  \"application\",\n\t\t\t\tsub: \"x.y\",\n\t\t\t},\n\t\t\twant: \"application.x.y\",\n\t\t},\n\t\t{\n\t\t\tname: \"namespace with format\",\n\t\t\targs: args{\n\t\t\t\tns:  \"app.yaml\",\n\t\t\t\tsub: \"x.y\",\n\t\t\t},\n\t\t\twant: \"app.x.y\",\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tif got := genKey(tt.args.ns, tt.args.sub); got != tt.want {\n\t\t\t\tt.Errorf(\"genKey() = %v, want %v\", got, tt.want)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc Test_format(t *testing.T) {\n\ttests := []struct {\n\t\tname      string\n\t\tnamespace string\n\t\twant      string\n\t}{\n\t\t{\n\t\t\tname:      \"properties namespace\",\n\t\t\tnamespace: \"application\",\n\t\t\twant:      \"json\",\n\t\t},\n\t\t{\n\t\t\tname:      \"properties namespace #1\",\n\t\t\tnamespace: \"app.setting\",\n\t\t\twant:      \"json\",\n\t\t},\n\t\t{\n\t\t\tname:      \"namespace with format[yaml]\",\n\t\t\tnamespace: \"app.yaml\",\n\t\t\twant:      \"yaml\",\n\t\t},\n\t\t{\n\t\t\tname:      \"namespace with format[yml]\",\n\t\t\tnamespace: \"app.yml\",\n\t\t\twant:      \"yml\",\n\t\t},\n\t\t{\n\t\t\tname:      \"namespace with format[json]\",\n\t\t\tnamespace: \"app.json\",\n\t\t\twant:      \"json\",\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tif got := format(tt.namespace); got != tt.want {\n\t\t\t\tt.Errorf(\"format() = %v, want %v\", got, tt.want)\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "contrib/config/apollo/go.mod",
    "content": "module github.com/go-kratos/kratos/contrib/config/apollo/v2\n\ngo 1.22\n\nrequire (\n\tgithub.com/apolloconfig/agollo/v4 v4.3.1\n\tgithub.com/go-kratos/kratos/v2 v2.9.2\n)\n\nrequire (\n\tdario.cat/mergo v1.0.0 // indirect\n\tgithub.com/fsnotify/fsnotify v1.6.0 // indirect\n\tgithub.com/hashicorp/hcl v1.0.0 // indirect\n\tgithub.com/magiconair/properties v1.8.6 // indirect\n\tgithub.com/mitchellh/mapstructure v1.4.3 // indirect\n\tgithub.com/pelletier/go-toml v1.9.4 // indirect\n\tgithub.com/pelletier/go-toml/v2 v2.0.0-beta.8 // indirect\n\tgithub.com/spf13/afero v1.8.2 // indirect\n\tgithub.com/spf13/cast v1.4.1 // indirect\n\tgithub.com/spf13/jwalterweatherman v1.1.0 // indirect\n\tgithub.com/spf13/pflag v1.0.5 // indirect\n\tgithub.com/spf13/viper v1.11.0 // indirect\n\tgithub.com/subosito/gotenv v1.2.0 // indirect\n\tgolang.org/x/sys v0.28.0 // indirect\n\tgolang.org/x/text v0.21.0 // indirect\n\tgoogle.golang.org/protobuf v1.33.0 // indirect\n\tgopkg.in/ini.v1 v1.66.4 // indirect\n\tgopkg.in/yaml.v2 v2.4.0 // indirect\n\tgopkg.in/yaml.v3 v3.0.1 // indirect\n)\n\nreplace github.com/go-kratos/kratos/v2 => ../../../\n"
  },
  {
    "path": "contrib/config/apollo/go.sum",
    "content": "cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=\ncloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=\ncloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=\ncloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=\ncloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=\ncloud.google.com/go v0.44.3/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=\ncloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=\ncloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=\ncloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To=\ncloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4=\ncloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M=\ncloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc=\ncloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk=\ncloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs=\ncloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc=\ncloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY=\ncloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI=\ncloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk=\ncloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY=\ncloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg=\ncloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8=\ncloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0=\ncloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=\ncloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=\ncloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=\ncloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=\ncloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=\ncloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=\ncloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=\ncloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=\ncloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk=\ncloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=\ncloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=\ncloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=\ncloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU=\ncloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=\ncloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=\ncloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=\ncloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=\ncloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=\ncloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo=\ndario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk=\ndario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=\ndmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=\ngithub.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=\ngithub.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=\ngithub.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=\ngithub.com/apolloconfig/agollo/v4 v4.3.1 h1:NHjd7KqOPmTvYwJidISc9MPBRO8m9UNrH3tijcEVNAY=\ngithub.com/apolloconfig/agollo/v4 v4.3.1/go.mod h1:n/7qxpKOTbegygLmO5OKmFWCdy3T+S/zioBGlo457Dk=\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-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=\ngithub.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=\ngithub.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM=\ngithub.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=\ngithub.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=\ngithub.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=\ngithub.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=\ngithub.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=\ngithub.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=\ngithub.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=\ngithub.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=\ngithub.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=\ngithub.com/coreos/go-systemd/v22 v22.3.2/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 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=\ngithub.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=\ngithub.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=\ngithub.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=\ngithub.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po=\ngithub.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=\ngithub.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=\ngithub.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=\ngithub.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=\ngithub.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=\ngithub.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=\ngithub.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=\ngithub.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=\ngithub.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=\ngithub.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=\ngithub.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=\ngithub.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=\ngithub.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=\ngithub.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=\ngithub.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=\ngithub.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=\ngithub.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=\ngithub.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=\ngithub.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=\ngithub.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=\ngithub.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=\ngithub.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=\ngithub.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=\ngithub.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=\ngithub.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8=\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.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=\ngithub.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=\ngithub.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=\ngithub.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=\ngithub.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=\ngithub.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=\ngithub.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=\ngithub.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=\ngithub.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=\ngithub.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=\ngithub.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=\ngithub.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=\ngithub.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM=\ngithub.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=\ngithub.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=\ngithub.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=\ngithub.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=\ngithub.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=\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.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.5.7 h1:81/ik6ipDQS2aGcBfIN5dHDB36BwrStyeAQquSYCV4o=\ngithub.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=\ngithub.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=\ngithub.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=\ngithub.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=\ngithub.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=\ngithub.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=\ngithub.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=\ngithub.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=\ngithub.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=\ngithub.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=\ngithub.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=\ngithub.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=\ngithub.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=\ngithub.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=\ngithub.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=\ngithub.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=\ngithub.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=\ngithub.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=\ngithub.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=\ngithub.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=\ngithub.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=\ngithub.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g=\ngithub.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=\ngithub.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=\ngithub.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q=\ngithub.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=\ngithub.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=\ngithub.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=\ngithub.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=\ngithub.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=\ngithub.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=\ngithub.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=\ngithub.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=\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.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=\ngithub.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=\ngithub.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=\ngithub.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=\ngithub.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=\ngithub.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=\ngithub.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=\ngithub.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=\ngithub.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=\ngithub.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=\ngithub.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=\ngithub.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=\ngithub.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=\ngithub.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=\ngithub.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=\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/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=\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 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=\ngithub.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=\ngithub.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=\ngithub.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamhfG/Qzo=\ngithub.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=\ngithub.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=\ngithub.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=\ngithub.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=\ngithub.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=\ngithub.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=\ngithub.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=\ngithub.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=\ngithub.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=\ngithub.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=\ngithub.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=\ngithub.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=\ngithub.com/mitchellh/mapstructure v1.4.3 h1:OVowDSCllw/YjdLkam3/sm7wEtOy59d8ndGgCcyj8cs=\ngithub.com/mitchellh/mapstructure v1.4.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=\ngithub.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/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/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=\ngithub.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=\ngithub.com/pelletier/go-toml v1.9.4 h1:tjENF6MfZAg8e4ZmZTeWaWiT2vXtsoO6+iuOjFhECwM=\ngithub.com/pelletier/go-toml v1.9.4/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=\ngithub.com/pelletier/go-toml/v2 v2.0.0-beta.8 h1:dy81yyLYJDwMTifq24Oi/IslOslRrDSb3jwDggjz3Z0=\ngithub.com/pelletier/go-toml/v2 v2.0.0-beta.8/go.mod h1:r9LEWfGN8R5k0VXJ+0BkIe7MYkRdwZOjgMj2KwnJFUo=\ngithub.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=\ngithub.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=\ngithub.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI=\ngithub.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg=\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/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=\ngithub.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=\ngithub.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=\ngithub.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=\ngithub.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=\ngithub.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=\ngithub.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=\ngithub.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=\ngithub.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=\ngithub.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=\ngithub.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I=\ngithub.com/spf13/afero v1.8.2 h1:xehSyVa0YnHWsJ49JFljMpg1HX19V6NDZ1fkm1Xznbo=\ngithub.com/spf13/afero v1.8.2/go.mod h1:CtAatgMJh6bJEIs48Ay/FOnkljP3WeGUG0MC1RfAqwo=\ngithub.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=\ngithub.com/spf13/cast v1.4.1 h1:s0hze+J0196ZfEMTs80N7UlFt0BDuQ7Q+JDnHiMWKdA=\ngithub.com/spf13/cast v1.4.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=\ngithub.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk=\ngithub.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=\ngithub.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=\ngithub.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=\ngithub.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns=\ngithub.com/spf13/viper v1.11.0 h1:7OX/1FS6n7jHD1zGrZTM7WtY13ZELRyosK4k93oPr44=\ngithub.com/spf13/viper v1.11.0/go.mod h1:djo0X/bA5+tYVoCn+C7cAYJGcVn/qYLFTG8gdUsX7Zk=\ngithub.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\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.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=\ngithub.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngithub.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngithub.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY=\ngithub.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngithub.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s=\ngithub.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=\ngithub.com/tevid/gohamcrest v1.1.1 h1:ou+xSqlIw1xfGTg1uq1nif/htZ2S3EzRqLm2BP+tYU0=\ngithub.com/tevid/gohamcrest v1.1.1/go.mod h1:3UvtWlqm8j5JbwYZh80D/PVBt0mJ1eJiYgZMibh0H/k=\ngithub.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=\ngithub.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=\ngithub.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=\ngithub.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=\ngithub.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=\ngo.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs=\ngo.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g=\ngo.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ=\ngo.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=\ngo.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=\ngo.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=\ngo.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=\ngo.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=\ngo.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=\ngo.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=\ngo.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=\ngo.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=\ngo.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo=\ngolang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/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-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\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.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=\ngolang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=\ngolang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=\ngolang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=\ngolang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=\ngolang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=\ngolang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=\ngolang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=\ngolang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=\ngolang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=\ngolang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=\ngolang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=\ngolang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=\ngolang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=\ngolang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=\ngolang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=\ngolang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=\ngolang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=\ngolang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=\ngolang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=\ngolang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=\ngolang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=\ngolang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=\ngolang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=\ngolang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=\ngolang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=\ngolang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=\ngolang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=\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/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=\ngolang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=\ngolang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=\ngolang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=\ngolang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20200222125558-5a598a2470a0/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-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=\ngolang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=\ngolang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=\ngolang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=\ngolang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=\ngolang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=\ngolang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=\ngolang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=\ngolang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=\ngolang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=\ngolang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=\ngolang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=\ngolang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=\ngolang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=\ngolang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=\ngolang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc=\ngolang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=\ngolang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=\ngolang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=\ngolang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=\ngolang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=\ngolang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=\ngolang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=\ngolang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=\ngolang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=\ngolang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=\ngolang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=\ngolang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=\ngolang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=\ngolang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\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-20190227155943-e225da77a7e6/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-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/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-20201207232520-09787c993a3a/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.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=\ngolang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=\ngolang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/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-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/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-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=\ngolang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=\ngolang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=\ngolang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/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.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\ngolang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\ngolang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=\ngolang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=\ngolang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=\ngolang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=\ngolang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=\ngolang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=\ngolang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=\ngolang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=\ngolang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=\ngolang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=\ngolang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=\ngolang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=\ngolang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=\ngolang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=\ngolang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=\ngolang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=\ngolang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/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-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=\ngolang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=\ngolang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=\ngolang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=\ngolang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=\ngolang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=\ngolang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=\ngolang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=\ngolang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=\ngolang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=\ngolang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=\ngolang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE=\ngolang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=\ngolang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=\ngolang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=\ngolang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=\ngolang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=\ngolang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=\ngolang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=\ngolang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=\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=\ngolang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f h1:GGU+dLjvlC3qDwqYgL6UgRmHXhOOgns0bZu2Ty5mm6U=\ngolang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngoogle.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=\ngoogle.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=\ngoogle.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=\ngoogle.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=\ngoogle.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=\ngoogle.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=\ngoogle.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=\ngoogle.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=\ngoogle.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=\ngoogle.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=\ngoogle.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=\ngoogle.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=\ngoogle.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=\ngoogle.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=\ngoogle.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM=\ngoogle.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc=\ngoogle.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg=\ngoogle.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE=\ngoogle.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8=\ngoogle.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU=\ngoogle.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94=\ngoogle.golang.org/api v0.44.0/go.mod h1:EBOGZqzyhtvMDoxwS97ctnh0zUmYY6CxqXsc1AvkYD8=\ngoogle.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=\ngoogle.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=\ngoogle.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=\ngoogle.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=\ngoogle.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=\ngoogle.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=\ngoogle.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=\ngoogle.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=\ngoogle.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=\ngoogle.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=\ngoogle.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=\ngoogle.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=\ngoogle.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=\ngoogle.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=\ngoogle.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=\ngoogle.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=\ngoogle.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=\ngoogle.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=\ngoogle.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=\ngoogle.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=\ngoogle.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=\ngoogle.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA=\ngoogle.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U=\ngoogle.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=\ngoogle.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=\ngoogle.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=\ngoogle.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=\ngoogle.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=\ngoogle.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=\ngoogle.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=\ngoogle.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=\ngoogle.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=\ngoogle.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=\ngoogle.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=\ngoogle.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=\ngoogle.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=\ngoogle.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=\ngoogle.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=\ngoogle.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=\ngoogle.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A=\ngoogle.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=\ngoogle.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=\ngoogle.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=\ngoogle.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=\ngoogle.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=\ngoogle.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=\ngoogle.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=\ngoogle.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=\ngoogle.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=\ngoogle.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60=\ngoogle.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=\ngoogle.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=\ngoogle.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=\ngoogle.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=\ngoogle.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=\ngoogle.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=\ngoogle.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8=\ngoogle.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=\ngoogle.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=\ngoogle.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=\ngoogle.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=\ngoogle.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=\ngoogle.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=\ngoogle.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=\ngoogle.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=\ngoogle.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=\ngoogle.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=\ngoogle.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=\ngoogle.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=\ngoogle.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=\ngoogle.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=\ngoogle.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=\ngoogle.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=\ngoogle.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=\ngoogle.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=\ngopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/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/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=\ngopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=\ngopkg.in/ini.v1 v1.66.4 h1:SsAcf+mM7mRZo2nJNGt8mZCjG8ZRaNGMURJw7BsIST4=\ngopkg.in/ini.v1 v1.66.4/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=\ngopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=\ngopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=\ngopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\ngopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\ngopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=\ngopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\nhonnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\nhonnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\nhonnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\nhonnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\nhonnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=\nhonnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=\nhonnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=\nrsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=\nrsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=\nrsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=\n"
  },
  {
    "path": "contrib/config/apollo/parser.go",
    "content": "package apollo\n\ntype jsonExtParser struct{}\n\nfunc (parser jsonExtParser) Parse(configContent any) (map[string]any, error) {\n\treturn map[string]any{\"content\": configContent}, nil\n}\n\ntype yamlExtParser struct{}\n\nfunc (parser yamlExtParser) Parse(configContent any) (map[string]any, error) {\n\treturn map[string]any{\"content\": configContent}, nil\n}\n"
  },
  {
    "path": "contrib/config/apollo/watcher.go",
    "content": "package apollo\n\nimport (\n\t\"context\"\n\t\"strings\"\n\n\t\"github.com/apolloconfig/agollo/v4/storage\"\n\n\t\"github.com/go-kratos/kratos/v2/config\"\n\t\"github.com/go-kratos/kratos/v2/encoding\"\n\t\"github.com/go-kratos/kratos/v2/log\"\n)\n\ntype watcher struct {\n\tout <-chan []*config.KeyValue\n\n\tctx      context.Context\n\tcancelFn func()\n}\n\ntype customChangeListener struct {\n\tin     chan<- []*config.KeyValue\n\tapollo *apollo\n}\n\nfunc (c *customChangeListener) onChange(namespace string, changes map[string]*storage.ConfigChange) []*config.KeyValue {\n\tkv := make([]*config.KeyValue, 0, 2)\n\tif strings.Contains(namespace, \".\") && !strings.HasSuffix(namespace, \".\"+properties) &&\n\t\t(format(namespace) == yaml || format(namespace) == yml || format(namespace) == json) {\n\t\tif value, ok := changes[\"content\"]; ok {\n\t\t\tkv = append(kv, &config.KeyValue{\n\t\t\t\tKey:    namespace,\n\t\t\t\tValue:  []byte(value.NewValue.(string)),\n\t\t\t\tFormat: format(namespace),\n\t\t\t})\n\n\t\t\treturn kv\n\t\t}\n\t}\n\n\tnext := make(map[string]any)\n\n\tfor key, change := range changes {\n\t\tresolve(genKey(namespace, key), change.NewValue, next)\n\t}\n\n\tf := format(namespace)\n\tcodec := encoding.GetCodec(f)\n\tval, err := codec.Marshal(next)\n\tif err != nil {\n\t\tlog.Warnf(\"apollo could not handle namespace %s: %v\", namespace, err)\n\t\treturn nil\n\t}\n\tkv = append(kv, &config.KeyValue{\n\t\tKey:    namespace,\n\t\tValue:  val,\n\t\tFormat: f,\n\t})\n\n\treturn kv\n}\n\nfunc (c *customChangeListener) OnChange(changeEvent *storage.ChangeEvent) {\n\tchange := c.onChange(changeEvent.Namespace, changeEvent.Changes)\n\tif len(change) == 0 {\n\t\treturn\n\t}\n\n\tc.in <- change\n}\n\nfunc (c *customChangeListener) OnNewestChange(_ *storage.FullChangeEvent) {}\n\nfunc newWatcher(a *apollo) (config.Watcher, error) {\n\tchangeCh := make(chan []*config.KeyValue)\n\tlistener := &customChangeListener{in: changeCh, apollo: a}\n\ta.client.AddChangeListener(listener)\n\n\tctx, cancel := context.WithCancel(context.Background())\n\treturn &watcher{\n\t\tout: changeCh,\n\n\t\tctx: ctx,\n\t\tcancelFn: func() {\n\t\t\ta.client.RemoveChangeListener(listener)\n\t\t\tcancel()\n\t\t},\n\t}, nil\n}\n\n// Next will be blocked until the Stop method is called\nfunc (w *watcher) Next() ([]*config.KeyValue, error) {\n\tselect {\n\tcase kv := <-w.out:\n\t\treturn kv, nil\n\tcase <-w.ctx.Done():\n\t\treturn nil, w.ctx.Err()\n\t}\n}\n\nfunc (w *watcher) Stop() error {\n\tif w.cancelFn != nil {\n\t\tw.cancelFn()\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "contrib/config/apollo/watcher_test.go",
    "content": "package apollo\n\nimport (\n\t\"testing\"\n\n\t\"github.com/apolloconfig/agollo/v4/storage\"\n\n\t\"github.com/go-kratos/kratos/v2/config\"\n\t\"github.com/go-kratos/kratos/v2/encoding\"\n)\n\nfunc Test_onChange(t *testing.T) {\n\ts := map[string]struct {\n\t\tName string `yaml:\"name\"`\n\t}{\n\t\t\"app\": {\n\t\t\tName: \"new\",\n\t\t},\n\t}\n\tcodec := encoding.GetCodec(yaml)\n\tval, _ := codec.Marshal(s)\n\tc := customChangeListener{}\n\ttests := []struct {\n\t\tname      string\n\t\tnamespace string\n\t\tchanges   map[string]*storage.ConfigChange\n\t\tkvs       []*config.KeyValue\n\t}{\n\t\t{\n\t\t\t\"test yaml onChange\",\n\t\t\t\"app.yaml\",\n\t\t\tmap[string]*storage.ConfigChange{\n\t\t\t\t\"name\": {\n\t\t\t\t\tOldValue:   \"old\",\n\t\t\t\t\tNewValue:   \"new\",\n\t\t\t\t\tChangeType: storage.MODIFIED,\n\t\t\t\t},\n\t\t\t},\n\t\t\t[]*config.KeyValue{\n\t\t\t\t{\n\t\t\t\t\tKey:    \"app.yaml\",\n\t\t\t\t\tValue:  val,\n\t\t\t\t\tFormat: yaml,\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\t\"test json onChange\",\n\t\t\t\"app.json\",\n\t\t\tmap[string]*storage.ConfigChange{\n\t\t\t\t\"content\": {\n\t\t\t\t\tOldValue:   `{\"name\":\"old\"}`,\n\t\t\t\t\tNewValue:   `{\"name\":\"new\"}`,\n\t\t\t\t\tChangeType: storage.MODIFIED,\n\t\t\t\t},\n\t\t\t},\n\t\t\t[]*config.KeyValue{\n\t\t\t\t{\n\t\t\t\t\tKey:    \"app.json\",\n\t\t\t\t\tValue:  []byte(`{\"name\":\"new\"}`),\n\t\t\t\t\tFormat: json,\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tkvs := c.onChange(tt.namespace, tt.changes)\n\t\t\tif len(kvs) != len(tt.kvs) {\n\t\t\t\tt.Errorf(\"len(kvs) = %v, want %v\", len(kvs), len(tt.kvs))\n\t\t\t}\n\t\t\tfor i := range kvs {\n\t\t\t\tif kvs[i].Format != tt.kvs[i].Format || kvs[i].Key != tt.kvs[i].Key || string(kvs[i].Value) != string(tt.kvs[i].Value) {\n\t\t\t\t\tt.Errorf(\"got %v, want %v\", kvs[i], tt.kvs[i])\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "contrib/config/consul/README.md",
    "content": "# Consul Config\n\n```go\nimport (\n    \"github.com/hashicorp/consul/api\"\n\n    \"github.com/go-kratos/kratos/contrib/config/consul/v2\"\n)\n\nfunc main() {\n    consulClient, err := api.NewClient(&api.Config{\n        Address: \"127.0.0.1:8500\",\n    })\n    if err != nil {\n        panic(err)\n    }\n    cs, err := consul.New(consulClient, consul.WithPath(\"app/cart/configs/\"))\n    // consul中需要标注文件后缀，kratos读取配置需要适配文件后缀\n    // The file suffix needs to be marked, and kratos needs to adapt the file suffix to read the configuration.\n    if err != nil {\n        panic(err)\n    }\n    c := config.New(config.WithSource(cs))\n}\n```\n"
  },
  {
    "path": "contrib/config/consul/config.go",
    "content": "package consul\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"path/filepath\"\n\t\"strings\"\n\n\t\"github.com/hashicorp/consul/api\"\n\n\t\"github.com/go-kratos/kratos/v2/config\"\n)\n\n// Option is consul config option.\ntype Option func(o *options)\n\ntype options struct {\n\tctx  context.Context\n\tpath string\n}\n\n// WithContext with registry context.\nfunc WithContext(ctx context.Context) Option {\n\treturn func(o *options) {\n\t\to.ctx = ctx\n\t}\n}\n\n// WithPath is config path\nfunc WithPath(p string) Option {\n\treturn func(o *options) {\n\t\to.path = p\n\t}\n}\n\ntype source struct {\n\tclient  *api.Client\n\toptions *options\n}\n\nfunc New(client *api.Client, opts ...Option) (config.Source, error) {\n\toptions := &options{\n\t\tctx:  context.Background(),\n\t\tpath: \"\",\n\t}\n\n\tfor _, opt := range opts {\n\t\topt(options)\n\t}\n\n\tif options.path == \"\" {\n\t\treturn nil, errors.New(\"path invalid\")\n\t}\n\n\treturn &source{\n\t\tclient:  client,\n\t\toptions: options,\n\t}, nil\n}\n\n// Load return the config values\nfunc (s *source) Load() ([]*config.KeyValue, error) {\n\tkv, _, err := s.client.KV().List(s.options.path, nil)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tpathPrefix := s.options.path\n\tif !strings.HasSuffix(s.options.path, \"/\") {\n\t\tpathPrefix = pathPrefix + \"/\"\n\t}\n\tkvs := make([]*config.KeyValue, 0)\n\tfor _, item := range kv {\n\t\tk := strings.TrimPrefix(item.Key, pathPrefix)\n\t\tif k == \"\" {\n\t\t\tcontinue\n\t\t}\n\t\tkvs = append(kvs, &config.KeyValue{\n\t\t\tKey:    k,\n\t\t\tValue:  item.Value,\n\t\t\tFormat: strings.TrimPrefix(filepath.Ext(k), \".\"),\n\t\t})\n\t}\n\treturn kvs, nil\n}\n\n// Watch return the watcher\nfunc (s *source) Watch() (config.Watcher, error) {\n\treturn newWatcher(s)\n}\n"
  },
  {
    "path": "contrib/config/consul/config_test.go",
    "content": "package consul\n\nimport (\n\t\"reflect\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/hashicorp/consul/api\"\n\n\t\"github.com/go-kratos/kratos/v2/config\"\n)\n\nconst testPath = \"kratos/test/config\"\n\nconst testKey = \"kratos/test/config/key\"\n\nfunc TestConfig(t *testing.T) {\n\tclient, err := api.NewClient(&api.Config{\n\t\tAddress: \"127.0.0.1:8500\",\n\t})\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tif _, err = client.KV().Put(&api.KVPair{Key: testKey, Value: []byte(\"test config\")}, nil); err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tsource, err := New(client, WithPath(testPath))\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tkvs, err := source.Load()\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tif len(kvs) != 1 || kvs[0].Key != \"key\" || string(kvs[0].Value) != \"test config\" {\n\t\tt.Fatal(\"config error\")\n\t}\n\n\tw, err := source.Watch()\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tdefer func() {\n\t\t_ = w.Stop()\n\t}()\n\n\tif _, err = client.KV().Put(&api.KVPair{Key: testKey, Value: []byte(\"new config\")}, nil); err != nil {\n\t\tt.Error(err)\n\t}\n\n\tif kvs, err = w.Next(); err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tif len(kvs) != 1 || kvs[0].Key != \"key\" || string(kvs[0].Value) != \"new config\" {\n\t\tt.Fatal(\"config error\")\n\t}\n\n\tif _, err := client.KV().Delete(testKey, nil); err != nil {\n\t\tt.Error(err)\n\t}\n}\n\nfunc TestExtToFormat(t *testing.T) {\n\tclient, err := api.NewClient(&api.Config{\n\t\tAddress: \"127.0.0.1:8500\",\n\t})\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\ttp := \"kratos/test/ext\"\n\ttn := \"a.bird.json\"\n\ttk := tp + \"/\" + tn\n\ttc := `{\"a\":1}`\n\tif _, err = client.KV().Put(&api.KVPair{Key: tk, Value: []byte(tc)}, nil); err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tsource, err := New(client, WithPath(tp))\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tkvs, err := source.Load()\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif !reflect.DeepEqual(len(kvs), 1) {\n\t\tt.Errorf(\"len(kvs) is %d\", len(kvs))\n\t}\n\tif !reflect.DeepEqual(tn, kvs[0].Key) {\n\t\tt.Errorf(\"kvs[0].Key is %s\", kvs[0].Key)\n\t}\n\tif !reflect.DeepEqual(tc, string(kvs[0].Value)) {\n\t\tt.Errorf(\"kvs[0].Value is %s\", kvs[0].Value)\n\t}\n\tif !reflect.DeepEqual(\"json\", kvs[0].Format) {\n\t\tt.Errorf(\"kvs[0].Format is %s\", kvs[0].Format)\n\t}\n}\n\nfunc Test_source_Watch(t *testing.T) {\n\tclient, err := api.NewClient(&api.Config{\n\t\tAddress: \"127.0.0.1:8500\",\n\t})\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tsource, err := New(client, WithPath(testPath))\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\ttype fields struct {\n\t\tsource config.Source\n\t}\n\n\ttype args struct {\n\t\tkey   string\n\t\tvalue string\n\t}\n\n\ttests := []struct {\n\t\tname      string\n\t\tfields    fields\n\t\targs      args\n\t\twant      string\n\t\twantErr   bool\n\t\tdeferFunc func(t *testing.T)\n\t}{\n\t\t{\n\t\t\tname:   \"normal\",\n\t\t\tfields: fields{source: source},\n\t\t\targs: args{\n\t\t\t\tkey:   testKey,\n\t\t\t\tvalue: \"test value\",\n\t\t\t},\n\t\t\twant:    \"test value\",\n\t\t\twantErr: false,\n\t\t\tdeferFunc: func(t *testing.T) {\n\t\t\t\t_, err := client.KV().Delete(testKey, nil)\n\t\t\t\tif err != nil {\n\t\t\t\t\tt.Error(err)\n\t\t\t\t}\n\t\t\t},\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tif tt.deferFunc != nil {\n\t\t\t\tdefer tt.deferFunc(t)\n\t\t\t}\n\n\t\t\tgot, err := tt.fields.source.Watch()\n\t\t\tif (err != nil) != tt.wantErr {\n\t\t\t\tt.Errorf(\"Watch() error = %v, wantErr %v\", err, tt.wantErr)\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\ttime.Sleep(100 * time.Millisecond)\n\t\t\t_, err = client.KV().Put(&api.KVPair{Key: tt.args.key, Value: []byte(tt.args.value)}, nil)\n\t\t\tif err != nil {\n\t\t\t\tt.Error(err)\n\t\t\t}\n\n\t\t\tnext, err := got.Next()\n\t\t\tif (err != nil) != tt.wantErr {\n\t\t\t\tt.Errorf(\"Watch() error = %v, wantErr %v\", err, tt.wantErr)\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tif len(next) != 1 {\n\t\t\t\tt.Error(\"watch is error\")\n\t\t\t}\n\n\t\t\tif !reflect.DeepEqual(string(next[0].Value), tt.want) {\n\t\t\t\tt.Errorf(\"Watch got = %v, want %v\", string(next[0].Value), tt.want)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc Test_source_Load(t *testing.T) {\n\tclient, err := api.NewClient(&api.Config{\n\t\tAddress: \"127.0.0.1:8500\",\n\t})\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tsource, err := New(client, WithPath(testPath))\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\ttype args struct {\n\t\tkey   string\n\t\tvalue string\n\t}\n\ttype fields struct {\n\t\tsource config.Source\n\t}\n\ttests := []struct {\n\t\tname      string\n\t\targs      args\n\t\tfields    fields\n\t\twant      []*config.KeyValue\n\t\twantErr   bool\n\t\tdeferFunc func(t *testing.T)\n\t}{\n\t\t{\n\t\t\tname: \"normal\",\n\t\t\targs: args{\n\t\t\t\tkey:   testKey,\n\t\t\t\tvalue: \"test value\",\n\t\t\t},\n\t\t\tfields: fields{\n\t\t\t\tsource: source,\n\t\t\t},\n\t\t\twant: []*config.KeyValue{\n\t\t\t\t{\n\t\t\t\t\tKey:   \"key\",\n\t\t\t\t\tValue: []byte(\"test value\"),\n\t\t\t\t},\n\t\t\t},\n\t\t\tdeferFunc: func(t *testing.T) {\n\t\t\t\t_, err1 := client.KV().Delete(testKey, nil)\n\t\t\t\tif err1 != nil {\n\t\t\t\t\tt.Error(err)\n\t\t\t\t}\n\t\t\t},\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tif tt.deferFunc != nil {\n\t\t\t\tdefer tt.deferFunc(t)\n\t\t\t}\n\t\t\t_, err = client.KV().Put(&api.KVPair{Key: tt.args.key, Value: []byte(tt.args.value)}, nil)\n\t\t\tif err != nil {\n\t\t\t\tt.Error(err)\n\t\t\t}\n\t\t\tgot, err := tt.fields.source.Load()\n\t\t\tif (err != nil) != tt.wantErr {\n\t\t\t\tt.Errorf(\"Load() error = %v, wantErr %v\", err, tt.wantErr)\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tif !reflect.DeepEqual(got[0], tt.want[0]) {\n\t\t\t\tt.Errorf(\"Load() got = %v, want %v\", got, tt.want)\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "contrib/config/consul/go.mod",
    "content": "module github.com/go-kratos/kratos/contrib/config/consul/v2\n\ngo 1.22\n\nrequire (\n\tgithub.com/go-kratos/kratos/v2 v2.9.2\n\tgithub.com/hashicorp/consul/api v1.26.1\n)\n\nrequire (\n\tdario.cat/mergo v1.0.0 // indirect\n\tgithub.com/armon/go-metrics v0.4.1 // indirect\n\tgithub.com/fatih/color v1.14.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/mattn/go-colorable v0.1.13 // indirect\n\tgithub.com/mattn/go-isatty v0.0.17 // indirect\n\tgithub.com/mitchellh/go-homedir v1.1.0 // indirect\n\tgithub.com/mitchellh/mapstructure v1.5.0 // indirect\n\tgolang.org/x/exp v0.0.0-20230817173708-d852ddb80c63 // indirect\n\tgolang.org/x/sys v0.28.0 // indirect\n\tgoogle.golang.org/protobuf v1.33.0 // indirect\n\tgopkg.in/yaml.v3 v3.0.1 // indirect\n)\n\nreplace github.com/go-kratos/kratos/v2 => ../../../\n"
  },
  {
    "path": "contrib/config/consul/go.sum",
    "content": "dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk=\ndario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=\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/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.14.1 h1:qfhVLaG5s+nCROl1zJsZRxFeYrHLqWroPOQ8BWiNb4w=\ngithub.com/fatih/color v1.14.1/go.mod h1:2oHN61fhTpgcxD3TSWCgKDiH1+x4OiDVVGH8WlgGZGg=\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-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=\ngithub.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=\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/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.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=\ngithub.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=\ngithub.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=\ngithub.com/hashicorp/consul/api v1.26.1 h1:5oSXOO5fboPZeW5SN+TdGFP/BILDgBm19OrPZ/pICIM=\ngithub.com/hashicorp/consul/api v1.26.1/go.mod h1:B4sQTeaSO16NtynqrAdwOlahJ7IUDZM9cj2420xYL8A=\ngithub.com/hashicorp/consul/sdk v0.15.0 h1:2qK9nDrr4tiJKRoxPGhm6B7xJjLVIQqkjiab2M4aKjU=\ngithub.com/hashicorp/consul/sdk v0.15.0/go.mod h1:r/OmRRPbHOe0yxNahLw7G9x5WG17E1BIECMtCjcPSNo=\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/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=\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.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng=\ngithub.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=\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/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/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=\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/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.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=\ngithub.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=\ngithub.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=\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/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.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY=\ngithub.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=\ngithub.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM=\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/exp v0.0.0-20230817173708-d852ddb80c63 h1:m64FZMko/V45gv0bNmrNYoDEq8U5YUhetc9cBWKS1TQ=\ngolang.org/x/exp v0.0.0-20230817173708-d852ddb80c63/go.mod h1:0v4NqG35kSWCMzLaMeX+IQrlSnVE/bqGSyC2cz/9Le8=\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-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=\ngolang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8=\ngolang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I=\ngolang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=\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-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=\ngolang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=\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-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-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.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=\ngolang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=\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/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/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngoogle.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=\ngoogle.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=\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": "contrib/config/consul/watcher.go",
    "content": "package consul\n\nimport (\n\t\"context\"\n\t\"path/filepath\"\n\t\"strings\"\n\n\t\"github.com/hashicorp/consul/api\"\n\t\"github.com/hashicorp/consul/api/watch\"\n\n\t\"github.com/go-kratos/kratos/v2/config\"\n)\n\ntype watcher struct {\n\tsource          *source\n\tch              chan []*config.KeyValue\n\twp              *watch.Plan\n\tfileModifyIndex map[string]uint64\n\tctx             context.Context\n\tcancel          context.CancelFunc\n}\n\nfunc (w *watcher) handle(_ uint64, data any) {\n\tif data == nil {\n\t\treturn\n\t}\n\n\tkv, ok := data.(api.KVPairs)\n\tif !ok {\n\t\treturn\n\t}\n\n\tpathPrefix := w.source.options.path\n\tif !strings.HasSuffix(w.source.options.path, \"/\") {\n\t\tpathPrefix = pathPrefix + \"/\"\n\t}\n\tkvs := make([]*config.KeyValue, 0, len(kv))\n\tfor _, item := range kv {\n\t\tif index, ok := w.fileModifyIndex[item.Key]; ok && item.ModifyIndex == index {\n\t\t\tcontinue\n\t\t}\n\t\tk := strings.TrimPrefix(item.Key, pathPrefix)\n\t\tif k == \"\" {\n\t\t\tcontinue\n\t\t}\n\t\tkvs = append(kvs, &config.KeyValue{\n\t\t\tKey:    k,\n\t\t\tValue:  item.Value,\n\t\t\tFormat: strings.TrimPrefix(filepath.Ext(k), \".\"),\n\t\t})\n\t\tw.fileModifyIndex[item.Key] = item.ModifyIndex\n\t}\n\n\tif len(kvs) == 0 {\n\t\treturn\n\t}\n\n\tw.ch <- kvs\n}\n\nfunc newWatcher(s *source) (*watcher, error) {\n\tctx, cancel := context.WithCancel(context.Background())\n\tw := &watcher{\n\t\tsource:          s,\n\t\tch:              make(chan []*config.KeyValue),\n\t\tfileModifyIndex: make(map[string]uint64),\n\t\tctx:             ctx,\n\t\tcancel:          cancel,\n\t}\n\n\twp, err := watch.Parse(map[string]any{\"type\": \"keyprefix\", \"prefix\": s.options.path})\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\twp.Handler = w.handle\n\tw.wp = wp\n\n\t// wp.Run is a blocking call and will prevent newWatcher from returning\n\tgo func() {\n\t\terr := wp.RunWithClientAndHclog(s.client, nil)\n\t\tif err != nil {\n\t\t\tpanic(err)\n\t\t}\n\t}()\n\n\treturn w, nil\n}\n\nfunc (w *watcher) Next() ([]*config.KeyValue, error) {\n\tselect {\n\tcase kv := <-w.ch:\n\t\treturn kv, nil\n\tcase <-w.ctx.Done():\n\t\treturn nil, w.ctx.Err()\n\t}\n}\n\nfunc (w *watcher) Stop() error {\n\tw.wp.Stop()\n\tw.cancel()\n\treturn nil\n}\n"
  },
  {
    "path": "contrib/config/etcd/README.md",
    "content": "# Etcd Config\n\n```go\nimport (\n\t\"log\"\n\n\tclientv3 \"go.etcd.io/etcd/client/v3\"\n\t\"google.golang.org/grpc\"\n\n\tcfg \"github.com/go-kratos/kratos/contrib/config/etcd/v2\"\n\t\"github.com/go-kratos/kratos/v2/config\"\n)\n\n// create an etcd client\nclient, err := clientv3.New(clientv3.Config{\n    Endpoints:   []string{\"127.0.0.1:2379\"},\n    DialTimeout: time.Second,\n    DialOptions: []grpc.DialOption{grpc.WithBlock()},\n})\nif err != nil {\n    log.Fatal(err)\n}\n\n// configure the source, \"path\" is required\nsource, err := cfg.New(client, cfg.WithPath(\"/app-config\"), cfg.WithPrefix(true))\nif err != nil {\n    log.Fatalln(err)\n}\n\n// create a config instance with source\nc := config.New(config.WithSource(source))\ndefer c.Close()\n\n// load sources before get\nif err := c.Load(); err != nil {\n    log.Fatalln(err)\n}\n\n// acquire config value\nfoo, err := c.Value(\"/app-config\").String()\nif err != nil {\n    log.Fatalln(err)\n}\n\nlog.Println(foo)\n```\n"
  },
  {
    "path": "contrib/config/etcd/config.go",
    "content": "package etcd\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"path/filepath\"\n\t\"strings\"\n\n\tclientv3 \"go.etcd.io/etcd/client/v3\"\n\n\t\"github.com/go-kratos/kratos/v2/config\"\n)\n\n// Option is etcd config option.\ntype Option func(o *options)\n\ntype options struct {\n\tctx    context.Context\n\tpath   string\n\tprefix bool\n}\n\n// WithContext with registry context.\nfunc WithContext(ctx context.Context) Option {\n\treturn func(o *options) {\n\t\to.ctx = ctx\n\t}\n}\n\n// WithPath is config path\nfunc WithPath(p string) Option {\n\treturn func(o *options) {\n\t\to.path = p\n\t}\n}\n\n// WithPrefix is config prefix\nfunc WithPrefix(prefix bool) Option {\n\treturn func(o *options) {\n\t\to.prefix = prefix\n\t}\n}\n\ntype source struct {\n\tclient  *clientv3.Client\n\toptions *options\n}\n\nfunc New(client *clientv3.Client, opts ...Option) (config.Source, error) {\n\toptions := &options{\n\t\tctx:    context.Background(),\n\t\tpath:   \"\",\n\t\tprefix: false,\n\t}\n\n\tfor _, opt := range opts {\n\t\topt(options)\n\t}\n\n\tif options.path == \"\" {\n\t\treturn nil, errors.New(\"path invalid\")\n\t}\n\n\treturn &source{\n\t\tclient:  client,\n\t\toptions: options,\n\t}, nil\n}\n\n// Load return the config values\nfunc (s *source) Load() ([]*config.KeyValue, error) {\n\tvar opts []clientv3.OpOption\n\tif s.options.prefix {\n\t\topts = append(opts, clientv3.WithPrefix())\n\t}\n\n\trsp, err := s.client.Get(s.options.ctx, s.options.path, opts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tkvs := make([]*config.KeyValue, 0, len(rsp.Kvs))\n\tfor _, item := range rsp.Kvs {\n\t\tk := string(item.Key)\n\t\tkvs = append(kvs, &config.KeyValue{\n\t\t\tKey:    k,\n\t\t\tValue:  item.Value,\n\t\t\tFormat: strings.TrimPrefix(filepath.Ext(k), \".\"),\n\t\t})\n\t}\n\treturn kvs, nil\n}\n\n// Watch return the watcher\nfunc (s *source) Watch() (config.Watcher, error) {\n\treturn newWatcher(s), nil\n}\n"
  },
  {
    "path": "contrib/config/etcd/config_test.go",
    "content": "package etcd\n\nimport (\n\t\"context\"\n\t\"reflect\"\n\t\"testing\"\n\t\"time\"\n\n\tclientv3 \"go.etcd.io/etcd/client/v3\"\n\t\"google.golang.org/grpc\"\n)\n\nconst testKey = \"/kratos/test/config\"\n\nfunc TestConfig(t *testing.T) {\n\tclient, err := clientv3.New(clientv3.Config{\n\t\tEndpoints:   []string{\"127.0.0.1:2379\"},\n\t\tDialTimeout: time.Second,\n\t\tDialOptions: []grpc.DialOption{grpc.WithBlock()},\n\t})\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tdefer func() {\n\t\t_ = client.Close()\n\t}()\n\tif _, err = client.Put(context.Background(), testKey, \"test config\"); err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tsource, err := New(client, WithPath(testKey))\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tkvs, err := source.Load()\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tif len(kvs) != 1 || kvs[0].Key != testKey || string(kvs[0].Value) != \"test config\" {\n\t\tt.Fatal(\"config error\")\n\t}\n\n\tw, err := source.Watch()\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tdefer func() {\n\t\t_ = w.Stop()\n\t}()\n\n\tif _, err = client.Put(context.Background(), testKey, \"new config\"); err != nil {\n\t\tt.Error(err)\n\t}\n\n\tif kvs, err = w.Next(); err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tif len(kvs) != 1 || kvs[0].Key != testKey || string(kvs[0].Value) != \"new config\" {\n\t\tt.Fatal(\"config error\")\n\t}\n\n\tif _, err := client.Delete(context.Background(), testKey); err != nil {\n\t\tt.Error(err)\n\t}\n}\n\nfunc TestExtToFormat(t *testing.T) {\n\tclient, err := clientv3.New(clientv3.Config{\n\t\tEndpoints:   []string{\"127.0.0.1:2379\"},\n\t\tDialTimeout: time.Second, DialOptions: []grpc.DialOption{grpc.WithBlock()},\n\t})\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tdefer func() {\n\t\t_ = client.Close()\n\t}()\n\n\ttp := \"/kratos/test/ext\"\n\ttn := \"a.bird.json\"\n\ttk := tp + \"/\" + tn\n\ttc := `{\"a\":1}`\n\tif _, err = client.Put(context.Background(), tk, tc); err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tsource, err := New(client, WithPath(tp), WithPrefix(true))\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tkvs, err := source.Load()\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif !reflect.DeepEqual(len(kvs), 1) {\n\t\tt.Errorf(\"len(kvs) = %d\", len(kvs))\n\t}\n\tif !reflect.DeepEqual(tk, kvs[0].Key) {\n\t\tt.Errorf(\"kvs[0].Key is %s\", kvs[0].Key)\n\t}\n\tif !reflect.DeepEqual(tc, string(kvs[0].Value)) {\n\t\tt.Errorf(\"kvs[0].Value is %s\", kvs[0].Value)\n\t}\n\tif !reflect.DeepEqual(\"json\", kvs[0].Format) {\n\t\tt.Errorf(\"kvs[0].Format is %s\", kvs[0].Format)\n\t}\n}\n\nfunc TestEtcdWithPath(t *testing.T) {\n\ttests := []struct {\n\t\tname   string\n\t\tfields string\n\t\twant   string\n\t}{\n\t\t{\n\t\t\tname:   \"default\",\n\t\t\tfields: testKey,\n\t\t\twant:   testKey,\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\toptions := &options{\n\t\t\t\tctx: context.Background(),\n\t\t\t}\n\n\t\t\tgot := WithPath(tt.fields)\n\t\t\tgot(options)\n\n\t\t\tif options.path != tt.want {\n\t\t\t\tt.Errorf(\"WithPath(tt.fields) = %v, want %v\", got, tt.want)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestEtcdWithPrefix(t *testing.T) {\n\ttests := []struct {\n\t\tname   string\n\t\tfields bool\n\t\twant   bool\n\t}{\n\t\t{\n\t\t\tname:   \"default\",\n\t\t\tfields: false,\n\t\t\twant:   false,\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\toptions := &options{\n\t\t\t\tctx: context.Background(),\n\t\t\t}\n\n\t\t\tgot := WithPrefix(tt.fields)\n\t\t\tgot(options)\n\n\t\t\tif options.prefix != tt.want {\n\t\t\t\tt.Errorf(\"WithPrefix(tt.fields) = %v, want %v\", got, tt.want)\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "contrib/config/etcd/go.mod",
    "content": "module github.com/go-kratos/kratos/contrib/config/etcd/v2\n\ngo 1.22\n\nrequire (\n\tgithub.com/go-kratos/kratos/v2 v2.9.2\n\tgo.etcd.io/etcd/client/v3 v3.5.11\n\tgoogle.golang.org/grpc v1.61.1\n)\n\nrequire (\n\tdario.cat/mergo v1.0.0 // indirect\n\tgithub.com/coreos/go-semver v0.3.0 // indirect\n\tgithub.com/coreos/go-systemd/v22 v22.3.2 // indirect\n\tgithub.com/gogo/protobuf v1.3.2 // indirect\n\tgithub.com/golang/protobuf v1.5.4 // indirect\n\tgithub.com/kr/text v0.2.0 // indirect\n\tgo.etcd.io/etcd/api/v3 v3.5.11 // indirect\n\tgo.etcd.io/etcd/client/pkg/v3 v3.5.11 // indirect\n\tgo.uber.org/atomic v1.7.0 // indirect\n\tgo.uber.org/multierr v1.6.0 // indirect\n\tgo.uber.org/zap v1.17.0 // indirect\n\tgolang.org/x/net v0.33.0 // indirect\n\tgolang.org/x/sys v0.28.0 // indirect\n\tgolang.org/x/text v0.21.0 // indirect\n\tgoogle.golang.org/genproto v0.0.0-20231212172506-995d672761c0 // indirect\n\tgoogle.golang.org/genproto/googleapis/api v0.0.0-20240102182953-50ed04b92917 // indirect\n\tgoogle.golang.org/genproto/googleapis/rpc v0.0.0-20240102182953-50ed04b92917 // indirect\n\tgoogle.golang.org/protobuf v1.33.0 // indirect\n\tgopkg.in/yaml.v3 v3.0.1 // indirect\n)\n\nreplace github.com/go-kratos/kratos/v2 => ../../../\n"
  },
  {
    "path": "contrib/config/etcd/go.sum",
    "content": "dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk=\ndario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=\ngithub.com/coreos/go-semver v0.3.0 h1:wkHLiw0WNATZnSG7epLsujiMCgPAc9xhjJ4tgnAxmfM=\ngithub.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=\ngithub.com/coreos/go-systemd/v22 v22.3.2 h1:D9/bQk5vlXQFZ6Kwuu6zaiXJ9oTPe68++AzAJc1DzSI=\ngithub.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=\ngithub.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=\ngithub.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.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/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=\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.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=\ngithub.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=\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/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/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/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=\ngithub.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=\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.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=\ngithub.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=\ngithub.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\ngithub.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=\ngithub.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngithub.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=\ngithub.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=\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.5.11 h1:B54KwXbWDHyD3XYAwprxNzTe7vlhR69LuBgZnMVvS7E=\ngo.etcd.io/etcd/api/v3 v3.5.11/go.mod h1:Ot+o0SWSyT6uHhA56al1oCED0JImsRiU9Dc26+C2a+4=\ngo.etcd.io/etcd/client/pkg/v3 v3.5.11 h1:bT2xVspdiCj2910T0V+/KHcVKjkUrCZVtk8J2JF2z1A=\ngo.etcd.io/etcd/client/pkg/v3 v3.5.11/go.mod h1:seTzl2d9APP8R5Y2hFL3NVlD6qC/dOT+3kvrqPyTas4=\ngo.etcd.io/etcd/client/v3 v3.5.11 h1:ajWtgoNSZJ1gmS8k+icvPtqsqEav+iUorF7b0qozgUU=\ngo.etcd.io/etcd/client/v3 v3.5.11/go.mod h1:a6xQUEqFJ8vztO1agJh/KQKOMfFI8og52ZconzcDJwE=\ngo.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw=\ngo.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=\ngo.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4=\ngo.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=\ngo.uber.org/zap v1.17.0 h1:MTjgFu6ZLKvY6Pvaqk97GlxNBuMpV4Hy/3P6tRGlI2U=\ngo.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo=\ngolang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=\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/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-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190620200207-3b0461eec859/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.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I=\ngolang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=\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.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=\ngolang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=\ngolang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/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-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=\ngolang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=\ngolang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\ngolang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=\ngolang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=\ngolang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\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=\ngoogle.golang.org/genproto v0.0.0-20231212172506-995d672761c0 h1:YJ5pD9rF8o9Qtta0Cmy9rdBwkSjrTCT6XTiUQVOtIos=\ngoogle.golang.org/genproto v0.0.0-20231212172506-995d672761c0/go.mod h1:l/k7rMz0vFTBPy+tFSGvXEd3z+BcoG1k7EHbqm+YBsY=\ngoogle.golang.org/genproto/googleapis/api v0.0.0-20240102182953-50ed04b92917 h1:rcS6EyEaoCO52hQDupoSfrxI3R6C2Tq741is7X8OvnM=\ngoogle.golang.org/genproto/googleapis/api v0.0.0-20240102182953-50ed04b92917/go.mod h1:CmlNWB9lSezaYELKS5Ym1r44VrrbPUa7JTvw+6MbpJ0=\ngoogle.golang.org/genproto/googleapis/rpc v0.0.0-20240102182953-50ed04b92917 h1:6G8oQ016D88m1xAKljMlBOOGWDZkes4kMhgGFlf8WcQ=\ngoogle.golang.org/genproto/googleapis/rpc v0.0.0-20240102182953-50ed04b92917/go.mod h1:xtjpI3tXFPP051KaWnhvxkiubL/6dJ18vLVf7q2pTOU=\ngoogle.golang.org/grpc v1.61.1 h1:kLAiWrZs7YeDM6MumDe7m3y4aM6wacLzM1Y/wiLP9XY=\ngoogle.golang.org/grpc v1.61.1/go.mod h1:VUbo7IFqmF1QtCAstipjG0GIoq49KvMe9+h1jFLBNJs=\ngoogle.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=\ngoogle.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=\ngopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/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.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=\ngopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=\ngopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\ngopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\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": "contrib/config/etcd/watcher.go",
    "content": "package etcd\n\nimport (\n\t\"context\"\n\n\tclientv3 \"go.etcd.io/etcd/client/v3\"\n\n\t\"github.com/go-kratos/kratos/v2/config\"\n)\n\ntype watcher struct {\n\tsource *source\n\tch     clientv3.WatchChan\n\n\tctx    context.Context\n\tcancel context.CancelFunc\n}\n\nfunc newWatcher(s *source) *watcher {\n\tctx, cancel := context.WithCancel(context.Background())\n\tw := &watcher{\n\t\tsource: s,\n\t\tctx:    ctx,\n\t\tcancel: cancel,\n\t}\n\n\tvar opts []clientv3.OpOption\n\tif s.options.prefix {\n\t\topts = append(opts, clientv3.WithPrefix())\n\t}\n\tw.ch = s.client.Watch(s.options.ctx, s.options.path, opts...)\n\n\treturn w\n}\n\nfunc (w *watcher) Next() ([]*config.KeyValue, error) {\n\tselect {\n\tcase resp := <-w.ch:\n\t\tif err := resp.Err(); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\treturn w.source.Load()\n\tcase <-w.ctx.Done():\n\t\treturn nil, w.ctx.Err()\n\t}\n}\n\nfunc (w *watcher) Stop() error {\n\tw.cancel()\n\treturn nil\n}\n"
  },
  {
    "path": "contrib/config/kubernetes/README.md",
    "content": "# Kubernetes Config\n\n### Usage in the Kubernetes Cluster\nIt is required to\n> serviceaccount should be set to the actual account of your environment, the default account will be `namespace::default` if the `spec.serviceAccount` is unset.\nexecute this command:\n```\nkubectl create clusterrolebinding go-kratos:kube --clusterrole=view --serviceaccount=mesh:default\n```\nor use `kubectl apply -f bind-role.yaml`\n```yaml\napiVersion: rbac.authorization.k8s.io/v1\nkind: ClusterRoleBinding\nmetadata:\n  name: go-kratos:kube\nroleRef:\n  apiGroup: rbac.authorization.k8s.io\n  kind: ClusterRole\n  name: view\nsubjects:\n- kind: ServiceAccount\n  name: default\n  namespace: mesh\n```\n\n### Usage outside the Kubernetes Cluster\nSet the path `~/.kube/config` to KubeConfig\n```go\nconfig.NewSource(SourceOption{\n\tNamespace:     \"mesh\",\n\tLabelSelector: \"\",\n\tKubeConfig:    filepath.Join(homedir.HomeDir(), \".kube\", \"config\"),\n})\n```\n"
  },
  {
    "path": "contrib/config/kubernetes/config.go",
    "content": "package kubernetes\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"path/filepath\"\n\t\"strings\"\n\n\tv1 \"k8s.io/api/core/v1\"\n\tmetav1 \"k8s.io/apimachinery/pkg/apis/meta/v1\"\n\t\"k8s.io/client-go/kubernetes\"\n\t\"k8s.io/client-go/rest\"\n\t\"k8s.io/client-go/tools/clientcmd\"\n\n\t\"github.com/go-kratos/kratos/v2/config\"\n)\n\n// Option is kubernetes option.\ntype Option func(*options)\n\ntype options struct {\n\t// kubernetes namespace\n\tNamespace string\n\t// kubernetes labelSelector example `app=test`\n\tLabelSelector string\n\t// kubernetes fieldSelector example `app=test`\n\tFieldSelector string\n\t// set KubeConfig out-of-cluster Use outside cluster\n\tKubeConfig string\n\t// set master url\n\tMaster string\n}\n\n// Namespace with kubernetes namespace.\nfunc Namespace(ns string) Option {\n\treturn func(o *options) {\n\t\to.Namespace = ns\n\t}\n}\n\n// LabelSelector with kubernetes label selector.\nfunc LabelSelector(label string) Option {\n\treturn func(o *options) {\n\t\to.LabelSelector = label\n\t}\n}\n\n// FieldSelector with kubernetes field selector.\nfunc FieldSelector(field string) Option {\n\treturn func(o *options) {\n\t\to.FieldSelector = field\n\t}\n}\n\n// KubeConfig with kubernetes config.\nfunc KubeConfig(config string) Option {\n\treturn func(o *options) {\n\t\to.KubeConfig = config\n\t}\n}\n\n// Master with kubernetes master.\nfunc Master(master string) Option {\n\treturn func(o *options) {\n\t\to.Master = master\n\t}\n}\n\ntype kube struct {\n\topts   options\n\tclient *kubernetes.Clientset\n}\n\n// NewSource new a kubernetes config source.\nfunc NewSource(opts ...Option) config.Source {\n\top := options{}\n\tfor _, o := range opts {\n\t\to(&op)\n\t}\n\treturn &kube{\n\t\topts: op,\n\t}\n}\n\nfunc (k *kube) init() (err error) {\n\tvar config *rest.Config\n\tif k.opts.KubeConfig != \"\" {\n\t\tif config, err = clientcmd.BuildConfigFromFlags(k.opts.Master, k.opts.KubeConfig); err != nil {\n\t\t\treturn err\n\t\t}\n\t} else {\n\t\tif config, err = rest.InClusterConfig(); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tif k.client, err = kubernetes.NewForConfig(config); err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc (k *kube) load() (kvs []*config.KeyValue, err error) {\n\tcmList, err := k.client.\n\t\tCoreV1().\n\t\tConfigMaps(k.opts.Namespace).\n\t\tList(context.Background(), metav1.ListOptions{\n\t\t\tLabelSelector: k.opts.LabelSelector,\n\t\t\tFieldSelector: k.opts.FieldSelector,\n\t\t})\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tfor _, cm := range cmList.Items {\n\t\tkvs = append(kvs, k.configMap(cm)...)\n\t}\n\treturn kvs, nil\n}\n\nfunc (k *kube) configMap(cm v1.ConfigMap) (kvs []*config.KeyValue) {\n\tfor name, val := range cm.Data {\n\t\tk := fmt.Sprintf(\"%s/%s/%s\", k.opts.Namespace, cm.Name, name)\n\n\t\tkvs = append(kvs, &config.KeyValue{\n\t\t\tKey:    k,\n\t\t\tValue:  []byte(val),\n\t\t\tFormat: strings.TrimPrefix(filepath.Ext(k), \".\"),\n\t\t})\n\t}\n\treturn kvs\n}\n\nfunc (k *kube) Load() ([]*config.KeyValue, error) {\n\tif k.opts.Namespace == \"\" {\n\t\treturn nil, errors.New(\"options namespace not full\")\n\t}\n\tif err := k.init(); err != nil {\n\t\treturn nil, err\n\t}\n\treturn k.load()\n}\n\nfunc (k *kube) Watch() (config.Watcher, error) {\n\treturn newWatcher(k)\n}\n"
  },
  {
    "path": "contrib/config/kubernetes/config_test.go",
    "content": "package kubernetes\n\nimport (\n\t\"context\"\n\t\"log\"\n\t\"path/filepath\"\n\t\"reflect\"\n\t\"strings\"\n\t\"testing\"\n\n\tv1 \"k8s.io/api/core/v1\"\n\tmetav1 \"k8s.io/apimachinery/pkg/apis/meta/v1\"\n\t\"k8s.io/client-go/kubernetes\"\n\t\"k8s.io/client-go/rest\"\n\t\"k8s.io/client-go/tools/clientcmd\"\n\t\"k8s.io/client-go/util/homedir\"\n\n\t\"github.com/go-kratos/kratos/v2/config\"\n)\n\nconst (\n\ttestKey   = \"test_config.json\"\n\tnamespace = \"default\"\n\tname      = \"test\"\n)\n\nvar (\n\tkeyPath    = strings.Join([]string{namespace, name, testKey}, \"/\")\n\tobjectMeta = metav1.ObjectMeta{\n\t\tName:      name,\n\t\tNamespace: namespace,\n\t\tLabels: map[string]string{\n\t\t\t\"app\": \"test\",\n\t\t},\n\t}\n)\n\nfunc TestSource(t *testing.T) {\n\thome := homedir.HomeDir()\n\ts := NewSource(\n\t\tNamespace(\"default\"),\n\t\tLabelSelector(\"\"),\n\t\tKubeConfig(filepath.Join(home, \".kube\", \"config\")),\n\t)\n\tkvs, err := s.Load()\n\tif err != nil {\n\t\tt.Error(err)\n\t}\n\tfor _, v := range kvs {\n\t\tt.Log(v)\n\t}\n}\n\nfunc ExampleNewSource() {\n\tconf := config.New(\n\t\tconfig.WithSource(\n\t\t\tNewSource(\n\t\t\t\tNamespace(\"mesh\"),\n\t\t\t\tLabelSelector(\"app=test\"),\n\t\t\t\tKubeConfig(filepath.Join(homedir.HomeDir(), \".kube\", \"config\")),\n\t\t\t),\n\t\t),\n\t)\n\terr := conf.Load()\n\tif err != nil {\n\t\tlog.Panic(err)\n\t}\n}\n\nfunc TestConfig(t *testing.T) {\n\trestConfig, err := rest.InClusterConfig()\n\thome := homedir.HomeDir()\n\n\toptions := []Option{\n\t\tNamespace(namespace),\n\t\tLabelSelector(\"app=test\"),\n\t}\n\n\tif err != nil {\n\t\tkubeconfig := filepath.Join(home, \".kube\", \"config\")\n\t\trestConfig, err = clientcmd.BuildConfigFromFlags(\"\", kubeconfig)\n\t\tif err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\t\toptions = append(options, KubeConfig(kubeconfig))\n\t}\n\tclientSet, err := kubernetes.NewForConfig(restConfig)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tclientSetConfigMaps := clientSet.CoreV1().ConfigMaps(namespace)\n\n\tsource := NewSource(options...)\n\tif _, err = clientSetConfigMaps.Create(context.Background(), &v1.ConfigMap{\n\t\tObjectMeta: objectMeta,\n\t\tData: map[string]string{\n\t\t\ttestKey: \"test config\",\n\t\t},\n\t}, metav1.CreateOptions{}); err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tdefer func() {\n\t\tif err = clientSetConfigMaps.Delete(context.Background(), name, metav1.DeleteOptions{}); err != nil {\n\t\t\tt.Error(err)\n\t\t}\n\t}()\n\tkvs, err := source.Load()\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif len(kvs) != 1 || kvs[0].Key != keyPath || string(kvs[0].Value) != \"test config\" {\n\t\tt.Fatal(\"config error\")\n\t}\n\n\tw, err := source.Watch()\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tdefer func() {\n\t\t_ = w.Stop()\n\t}()\n\t// create also produce an event, discard it\n\tif _, err = w.Next(); err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tif _, err = clientSetConfigMaps.Update(context.Background(), &v1.ConfigMap{\n\t\tObjectMeta: objectMeta,\n\t\tData: map[string]string{\n\t\t\ttestKey: \"new config\",\n\t\t},\n\t}, metav1.UpdateOptions{}); err != nil {\n\t\tt.Error(err)\n\t}\n\n\tif kvs, err = w.Next(); err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tif len(kvs) != 1 || kvs[0].Key != keyPath || string(kvs[0].Value) != \"new config\" {\n\t\tt.Fatal(\"config error\")\n\t}\n}\n\nfunc TestExtToFormat(t *testing.T) {\n\trestConfig, err := rest.InClusterConfig()\n\thome := homedir.HomeDir()\n\n\toptions := []Option{\n\t\tNamespace(namespace),\n\t\tLabelSelector(\"app=test\"),\n\t}\n\n\tif err != nil {\n\t\tkubeconfig := filepath.Join(home, \".kube\", \"config\")\n\t\trestConfig, err = clientcmd.BuildConfigFromFlags(\"\", kubeconfig)\n\t\tif err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\t\toptions = append(options, KubeConfig(kubeconfig))\n\t}\n\tclientSet, err := kubernetes.NewForConfig(restConfig)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tclientSetConfigMaps := clientSet.CoreV1().ConfigMaps(namespace)\n\n\ttc := `{\"a\":1}`\n\tif _, err = clientSetConfigMaps.Create(context.Background(), &v1.ConfigMap{\n\t\tObjectMeta: objectMeta,\n\t\tData: map[string]string{\n\t\t\ttestKey: tc,\n\t\t},\n\t}, metav1.CreateOptions{}); err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tdefer func() {\n\t\tif err = clientSetConfigMaps.Delete(context.Background(), name, metav1.DeleteOptions{}); err != nil {\n\t\t\tt.Error(err)\n\t\t}\n\t}()\n\n\tsource := NewSource(options...)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tkvs, err := source.Load()\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif !reflect.DeepEqual(len(kvs), 1) {\n\t\tt.Errorf(\"len(kvs) = %d\", len(kvs))\n\t}\n\tif !reflect.DeepEqual(keyPath, kvs[0].Key) {\n\t\tt.Errorf(\"kvs[0].Key is %s\", kvs[0].Key)\n\t}\n\tif !reflect.DeepEqual(tc, string(kvs[0].Value)) {\n\t\tt.Errorf(\"kvs[0].Value is %s\", kvs[0].Value)\n\t}\n\tif !reflect.DeepEqual(\"json\", kvs[0].Format) {\n\t\tt.Errorf(\"kvs[0].Format is %s\", kvs[0].Format)\n\t}\n}\n"
  },
  {
    "path": "contrib/config/kubernetes/go.mod",
    "content": "module github.com/go-kratos/kratos/contrib/config/kubernetes/v2\n\ngo 1.22\n\nrequire (\n\tgithub.com/go-kratos/kratos/v2 v2.9.2\n\tk8s.io/api v0.26.3\n\tk8s.io/apimachinery v0.26.3\n\tk8s.io/client-go v0.26.3\n)\n\nrequire (\n\tdario.cat/mergo v1.0.0 // indirect\n\tgithub.com/davecgh/go-spew v1.1.1 // indirect\n\tgithub.com/emicklei/go-restful/v3 v3.9.0 // indirect\n\tgithub.com/go-logr/logr v1.4.1 // indirect\n\tgithub.com/go-openapi/jsonpointer v0.19.5 // indirect\n\tgithub.com/go-openapi/jsonreference v0.20.0 // indirect\n\tgithub.com/go-openapi/swag v0.19.14 // indirect\n\tgithub.com/gogo/protobuf v1.3.2 // indirect\n\tgithub.com/golang/protobuf v1.5.4 // indirect\n\tgithub.com/google/gnostic v0.5.7-v3refs // indirect\n\tgithub.com/google/go-cmp v0.5.9 // indirect\n\tgithub.com/google/gofuzz v1.1.0 // indirect\n\tgithub.com/imdario/mergo v0.3.6 // indirect\n\tgithub.com/josharian/intern v1.0.0 // indirect\n\tgithub.com/json-iterator/go v1.1.12 // indirect\n\tgithub.com/mailru/easyjson v0.7.6 // 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/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect\n\tgithub.com/spf13/pflag v1.0.5 // indirect\n\tgolang.org/x/net v0.33.0 // indirect\n\tgolang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b // indirect\n\tgolang.org/x/sys v0.28.0 // indirect\n\tgolang.org/x/term v0.27.0 // indirect\n\tgolang.org/x/text v0.21.0 // indirect\n\tgolang.org/x/time v0.0.0-20220210224613-90d013bbcef8 // indirect\n\tgoogle.golang.org/appengine v1.6.7 // indirect\n\tgoogle.golang.org/protobuf v1.33.0 // indirect\n\tgopkg.in/inf.v0 v0.9.1 // indirect\n\tgopkg.in/yaml.v2 v2.4.0 // indirect\n\tgopkg.in/yaml.v3 v3.0.1 // indirect\n\tk8s.io/klog/v2 v2.80.1 // indirect\n\tk8s.io/kube-openapi v0.0.0-20221012153701-172d655c2280 // indirect\n\tk8s.io/utils v0.0.0-20221107191617-1a15be271d1d // indirect\n\tsigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 // indirect\n\tsigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect\n\tsigs.k8s.io/yaml v1.3.0 // indirect\n)\n\nreplace github.com/go-kratos/kratos/v2 => ../../../\n"
  },
  {
    "path": "contrib/config/kubernetes/go.sum",
    "content": "cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=\ncloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=\ncloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=\ncloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=\ncloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=\ncloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=\ncloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=\ncloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To=\ncloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4=\ncloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M=\ncloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc=\ncloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk=\ncloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs=\ncloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc=\ncloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY=\ncloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=\ncloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=\ncloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=\ncloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=\ncloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=\ncloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=\ncloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=\ncloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=\ncloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=\ncloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=\ncloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=\ncloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU=\ncloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=\ncloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=\ncloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=\ncloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=\ncloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=\ndario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk=\ndario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=\ndmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=\ngithub.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=\ngithub.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=\ngithub.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=\ngithub.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=\ngithub.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=\ngithub.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=\ngithub.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=\ngithub.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=\ngithub.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=\ngithub.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.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/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE=\ngithub.com/emicklei/go-restful/v3 v3.9.0 h1:XwGDlfxEnQZzuopoqxwSEllNcCOM9DhhFyhFIIGKwxE=\ngithub.com/emicklei/go-restful/v3 v3.9.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=\ngithub.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=\ngithub.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=\ngithub.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=\ngithub.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=\ngithub.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=\ngithub.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=\ngithub.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=\ngithub.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=\ngithub.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ=\ngithub.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=\ngithub.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=\ngithub.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY=\ngithub.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=\ngithub.com/go-openapi/jsonreference v0.20.0 h1:MYlu0sBgChmCfJxxUKZ8g1cPWFOB37YSZqewK7OKeyA=\ngithub.com/go-openapi/jsonreference v0.20.0/go.mod h1:Ag74Ico3lPc+zR+qjn4XBUmXymS4zJbYVCZmcgkasdo=\ngithub.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=\ngithub.com/go-openapi/swag v0.19.14 h1:gm3vOOXfiuw5i9p5N9xJvfjvuofpyvLA9Wr6QfK5Fng=\ngithub.com/go-openapi/swag v0.19.14/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ=\ngithub.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=\ngithub.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=\ngithub.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=\ngithub.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=\ngithub.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=\ngithub.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=\ngithub.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=\ngithub.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=\ngithub.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=\ngithub.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=\ngithub.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=\ngithub.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=\ngithub.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=\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.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=\ngithub.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=\ngithub.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=\ngithub.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=\ngithub.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=\ngithub.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=\ngithub.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=\ngithub.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=\ngithub.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=\ngithub.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=\ngithub.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=\ngithub.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=\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.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=\ngithub.com/google/gnostic v0.5.7-v3refs h1:FhTMOKj2VhjpouxvWJAV1TL304uMlb9zcDqkl6cEI54=\ngithub.com/google/gnostic v0.5.7-v3refs/go.mod h1:73MKFl6jIHelAJNaBGFzt3SPtZULs9dYrGFt8OiIsHQ=\ngithub.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=\ngithub.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=\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.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=\ngithub.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=\ngithub.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=\ngithub.com/google/gofuzz v1.1.0 h1:Hsa8mG0dQ46ij8Sl2AYJDUv1oA9/d6Vk+3LG99Oe02g=\ngithub.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=\ngithub.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=\ngithub.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=\ngithub.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=\ngithub.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=\ngithub.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=\ngithub.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=\ngithub.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=\ngithub.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=\ngithub.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=\ngithub.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=\ngithub.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=\ngithub.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=\ngithub.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=\ngithub.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=\ngithub.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=\ngithub.com/imdario/mergo v0.3.6 h1:xTNEAn+kxVO7dTZGu0CegyqKZmoWFI0rF8UxjlB2d28=\ngithub.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=\ngithub.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=\ngithub.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=\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/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=\ngithub.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=\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/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=\ngithub.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=\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/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=\ngithub.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=\ngithub.com/mailru/easyjson v0.7.6 h1:8yTIVnZgCoiM1TgqoeTl+LfU5Jg6/xL3QhGQnimLYnA=\ngithub.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=\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 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=\ngithub.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=\ngithub.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=\ngithub.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=\ngithub.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=\ngithub.com/onsi/ginkgo/v2 v2.4.0 h1:+Ig9nvqgS5OBSACXNk15PLdp0U9XPYROt9CFzVdFGIs=\ngithub.com/onsi/ginkgo/v2 v2.4.0/go.mod h1:iHkDK1fKGcBoEHT5W7YBq4RFWaQulw+caOMkAt4OrFo=\ngithub.com/onsi/gomega v1.23.0 h1:/oxKu9c2HVap+F3PfKort2Hw5DEU+HGlW8n+tguWsys=\ngithub.com/onsi/gomega v1.23.0/go.mod h1:Z/NWtiqwBrwUt4/2loMmHL63EDLnYHmVbuBpDr2vQAg=\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/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=\ngithub.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=\ngithub.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=\ngithub.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=\ngithub.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=\ngithub.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=\ngithub.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8=\ngithub.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\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.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=\ngithub.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngithub.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=\ngithub.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=\ngithub.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=\ngithub.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=\ngithub.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=\ngithub.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=\ngo.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=\ngo.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=\ngo.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=\ngo.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=\ngo.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=\ngolang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=\ngolang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\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/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=\ngolang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=\ngolang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=\ngolang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=\ngolang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=\ngolang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=\ngolang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=\ngolang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=\ngolang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=\ngolang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=\ngolang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=\ngolang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=\ngolang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=\ngolang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=\ngolang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=\ngolang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=\ngolang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=\ngolang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=\ngolang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=\ngolang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=\ngolang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=\ngolang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=\ngolang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=\ngolang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=\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-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=\ngolang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20200222125558-5a598a2470a0/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-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=\ngolang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=\ngolang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=\ngolang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=\ngolang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=\ngolang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=\ngolang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=\ngolang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=\ngolang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=\ngolang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=\ngolang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I=\ngolang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=\ngolang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=\ngolang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=\ngolang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=\ngolang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=\ngolang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=\ngolang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b h1:clP8eMhB30EHdc0bd2Twtq6kgU7yl5ub2cQLSdrv1Dg=\ngolang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc=\ngolang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\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-20190227155943-e225da77a7e6/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-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/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.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=\ngolang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=\ngolang.org/x/sys v0.0.0-20180830151530-49385e6e1522/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-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=\ngolang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=\ngolang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=\ngolang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q=\ngolang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM=\ngolang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/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.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=\ngolang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=\ngolang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=\ngolang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=\ngolang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=\ngolang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=\ngolang.org/x/time v0.0.0-20220210224613-90d013bbcef8 h1:vVKdlvoWBphwdxWKrFZEuM0kGgGLxUOYcY4U/2Vjg44=\ngolang.org/x/time v0.0.0-20220210224613-90d013bbcef8/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=\ngolang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=\ngolang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=\ngolang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=\ngolang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=\ngolang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=\ngolang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=\ngolang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=\ngolang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=\ngolang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=\ngolang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=\ngolang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/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-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=\ngolang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=\ngolang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=\ngolang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=\ngolang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=\ngolang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=\ngolang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=\ngolang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=\ngolang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=\ngolang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=\ngolang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=\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=\ngoogle.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=\ngoogle.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=\ngoogle.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=\ngoogle.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=\ngoogle.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=\ngoogle.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=\ngoogle.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=\ngoogle.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=\ngoogle.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=\ngoogle.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=\ngoogle.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=\ngoogle.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=\ngoogle.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=\ngoogle.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=\ngoogle.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM=\ngoogle.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc=\ngoogle.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=\ngoogle.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=\ngoogle.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=\ngoogle.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=\ngoogle.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=\ngoogle.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=\ngoogle.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=\ngoogle.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=\ngoogle.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=\ngoogle.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=\ngoogle.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=\ngoogle.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=\ngoogle.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=\ngoogle.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=\ngoogle.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=\ngoogle.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=\ngoogle.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=\ngoogle.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=\ngoogle.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=\ngoogle.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=\ngoogle.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=\ngoogle.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=\ngoogle.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA=\ngoogle.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U=\ngoogle.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=\ngoogle.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=\ngoogle.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=\ngoogle.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=\ngoogle.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=\ngoogle.golang.org/genproto v0.0.0-20201019141844-1ed22bb0c154/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=\ngoogle.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=\ngoogle.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=\ngoogle.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=\ngoogle.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=\ngoogle.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=\ngoogle.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=\ngoogle.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=\ngoogle.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=\ngoogle.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60=\ngoogle.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=\ngoogle.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=\ngoogle.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=\ngoogle.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=\ngoogle.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=\ngoogle.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=\ngoogle.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=\ngoogle.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=\ngoogle.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=\ngoogle.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=\ngoogle.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=\ngoogle.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=\ngoogle.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=\ngoogle.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=\ngoogle.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=\ngoogle.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=\ngoogle.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=\ngopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/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-20200227125254-8fa46927fb4f/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/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=\ngopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=\ngopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=\ngopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=\ngopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=\ngopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\ngopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\ngopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=\ngopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\nhonnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\nhonnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\nhonnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\nhonnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\nhonnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=\nhonnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=\nhonnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=\nk8s.io/api v0.26.3 h1:emf74GIQMTik01Aum9dPP0gAypL8JTLl/lHa4V9RFSU=\nk8s.io/api v0.26.3/go.mod h1:PXsqwPMXBSBcL1lJ9CYDKy7kIReUydukS5JiRlxC3qE=\nk8s.io/apimachinery v0.26.3 h1:dQx6PNETJ7nODU3XPtrwkfuubs6w7sX0M8n61zHIV/k=\nk8s.io/apimachinery v0.26.3/go.mod h1:ats7nN1LExKHvJ9TmwootT00Yz05MuYqPXEXaVeOy5I=\nk8s.io/client-go v0.26.3 h1:k1UY+KXfkxV2ScEL3gilKcF7761xkYsSD6BC9szIu8s=\nk8s.io/client-go v0.26.3/go.mod h1:ZPNu9lm8/dbRIPAgteN30RSXea6vrCpFvq+MateTUuQ=\nk8s.io/klog/v2 v2.80.1 h1:atnLQ121W371wYYFawwYx1aEY2eUfs4l3J72wtgAwV4=\nk8s.io/klog/v2 v2.80.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0=\nk8s.io/kube-openapi v0.0.0-20221012153701-172d655c2280 h1:+70TFaan3hfJzs+7VK2o+OGxg8HsuBr/5f6tVAjDu6E=\nk8s.io/kube-openapi v0.0.0-20221012153701-172d655c2280/go.mod h1:+Axhij7bCpeqhklhUTe3xmOn6bWxolyZEeyaFpjGtl4=\nk8s.io/utils v0.0.0-20221107191617-1a15be271d1d h1:0Smp/HP1OH4Rvhe+4B8nWGERtlqAGSftbSbbmm45oFs=\nk8s.io/utils v0.0.0-20221107191617-1a15be271d1d/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=\nrsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=\nrsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=\nrsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=\nsigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 h1:iXTIw73aPyC+oRdyqqvVJuloN1p0AC/kzH07hu3NE+k=\nsigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0=\nsigs.k8s.io/structured-merge-diff/v4 v4.2.3 h1:PRbqxJClWWYMNV1dhaG4NsibJbArud9kFxnAMREiWFE=\nsigs.k8s.io/structured-merge-diff/v4 v4.2.3/go.mod h1:qjx8mGObPmV2aSZepjQjbmb2ihdVs8cGKBraizNC69E=\nsigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo=\nsigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8=\n"
  },
  {
    "path": "contrib/config/kubernetes/watcher.go",
    "content": "package kubernetes\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\tv1 \"k8s.io/api/core/v1\"\n\tmetav1 \"k8s.io/apimachinery/pkg/apis/meta/v1\"\n\t\"k8s.io/apimachinery/pkg/watch\"\n\n\t\"github.com/go-kratos/kratos/v2/config\"\n)\n\ntype watcher struct {\n\tk       *kube\n\twatcher watch.Interface\n}\n\nfunc newWatcher(k *kube) (config.Watcher, error) {\n\tw, err := k.client.CoreV1().ConfigMaps(k.opts.Namespace).Watch(context.Background(), metav1.ListOptions{\n\t\tLabelSelector: k.opts.LabelSelector,\n\t\tFieldSelector: k.opts.FieldSelector,\n\t})\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn &watcher{\n\t\tk:       k,\n\t\twatcher: w,\n\t}, nil\n}\n\nfunc (w *watcher) Next() ([]*config.KeyValue, error) {\nResultChan:\n\tch := <-w.watcher.ResultChan()\n\tif ch.Object == nil {\n\t\t// recreate the watcher\n\t\tk8sWatcher, err := w.k.client.CoreV1().ConfigMaps(w.k.opts.Namespace).Watch(context.Background(), metav1.ListOptions{\n\t\t\tLabelSelector: w.k.opts.LabelSelector,\n\t\t\tFieldSelector: w.k.opts.FieldSelector,\n\t\t})\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tw.watcher = k8sWatcher\n\t\tgoto ResultChan\n\t}\n\tcm, ok := ch.Object.(*v1.ConfigMap)\n\tif !ok {\n\t\treturn nil, fmt.Errorf(\"kubernetes Object not ConfigMap\")\n\t}\n\tif ch.Type == \"DELETED\" {\n\t\treturn nil, fmt.Errorf(\"kubernetes configmap delete %s\", cm.Name)\n\t}\n\treturn w.k.configMap(*cm), nil\n}\n\nfunc (w *watcher) Stop() error {\n\tw.watcher.Stop()\n\treturn nil\n}\n"
  },
  {
    "path": "contrib/config/kubernetes/watcher_test.go",
    "content": "package kubernetes\n\nimport (\n\t\"context\"\n\t\"path/filepath\"\n\t\"testing\"\n\t\"time\"\n\n\tmetav1 \"k8s.io/apimachinery/pkg/apis/meta/v1\"\n\t\"k8s.io/client-go/kubernetes\"\n\t\"k8s.io/client-go/tools/clientcmd\"\n\t\"k8s.io/client-go/util/homedir\"\n)\n\nfunc TestKube(t *testing.T) {\n\thome := homedir.HomeDir()\n\tconfig, err := clientcmd.BuildConfigFromFlags(\"\", filepath.Join(home, \".kube\", \"config\"))\n\tif err != nil {\n\t\tt.Error(err)\n\t}\n\tclient, err := kubernetes.NewForConfig(config)\n\tif err != nil {\n\t\tt.Error(err)\n\t}\n\tcmWatcher, err := client.CoreV1().ConfigMaps(\"mesh\").Watch(context.Background(), metav1.ListOptions{\n\t\tLabelSelector: \"app=test\",\n\t\t// FieldSelector:        \"\",\n\t})\n\tif err != nil {\n\t\tt.Error(err)\n\t}\n\tgo func() {\n\t\ttime.Sleep(5 * time.Second)\n\t\tcmWatcher.Stop()\n\t}()\n\tfor c := range cmWatcher.ResultChan() {\n\t\tif c.Object == nil {\n\t\t\treturn\n\t\t}\n\t\tt.Log(c.Type, c.Object)\n\t}\n}\n"
  },
  {
    "path": "contrib/config/nacos/README.md",
    "content": "# Nacos Config\n\n```go\nimport (\n\t\"github.com/nacos-group/nacos-sdk-go/clients\"\n\t\"github.com/nacos-group/nacos-sdk-go/common/constant\"\n\n\tkconfig \"github.com/go-kratos/kratos/v2/config\"\n)\n\nsc := []constant.ServerConfig{\n\t*constant.NewServerConfig(\"127.0.0.1\", 8848),\n}\n\ncc := &constant.ClientConfig{\n\tNamespaceId:         \"public\", //namespace id\n\tTimeoutMs:           5000,\n\tNotLoadCacheAtStart: true,\n\tLogDir:              \"/tmp/nacos/log\",\n\tCacheDir:            \"/tmp/nacos/cache\",\n\tRotateTime:          \"1h\",\n\tMaxAge:              3,\n\tLogLevel:            \"debug\",\n}\n\n// a more graceful way to create naming client\nclient, err := clients.NewConfigClient(\n\tvo.NacosClientParam{\n\t\tClientConfig:  cc,\n\t\tServerConfigs: sc,\n\t},\n)\nif err != nil {\n\tlog.Panic(err)\n}\n```\n"
  },
  {
    "path": "contrib/config/nacos/config.go",
    "content": "package config\n\nimport (\n\t\"context\"\n\t\"path/filepath\"\n\t\"strings\"\n\n\t\"github.com/nacos-group/nacos-sdk-go/clients/config_client\"\n\t\"github.com/nacos-group/nacos-sdk-go/vo\"\n\n\t\"github.com/go-kratos/kratos/v2/config\"\n)\n\ntype Option func(*options)\n\ntype options struct {\n\tgroup  string\n\tdataID string\n}\n\n// WithGroup With nacos config group.\nfunc WithGroup(group string) Option {\n\treturn func(o *options) {\n\t\to.group = group\n\t}\n}\n\n// WithDataID With nacos config data id.\nfunc WithDataID(dataID string) Option {\n\treturn func(o *options) {\n\t\to.dataID = dataID\n\t}\n}\n\ntype Config struct {\n\topts   options\n\tclient config_client.IConfigClient\n}\n\nfunc NewConfigSource(client config_client.IConfigClient, opts ...Option) config.Source {\n\t_options := options{}\n\tfor _, o := range opts {\n\t\to(&_options)\n\t}\n\treturn &Config{client: client, opts: _options}\n}\n\nfunc (c *Config) Load() ([]*config.KeyValue, error) {\n\tcontent, err := c.client.GetConfig(vo.ConfigParam{\n\t\tDataId: c.opts.dataID,\n\t\tGroup:  c.opts.group,\n\t})\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tk := c.opts.dataID\n\treturn []*config.KeyValue{\n\t\t{\n\t\t\tKey:    k,\n\t\t\tValue:  []byte(content),\n\t\t\tFormat: strings.TrimPrefix(filepath.Ext(k), \".\"),\n\t\t},\n\t}, nil\n}\n\nfunc (c *Config) Watch() (config.Watcher, error) {\n\twatcher := newWatcher(context.Background(), c.opts.dataID, c.opts.group, c.client.CancelListenConfig)\n\terr := c.client.ListenConfig(vo.ConfigParam{\n\t\tDataId: c.opts.dataID,\n\t\tGroup:  c.opts.group,\n\t\tOnChange: func(_, group, dataId, data string) {\n\t\t\tif dataId == watcher.dataID && group == watcher.group {\n\t\t\t\twatcher.content <- data\n\t\t\t}\n\t\t},\n\t})\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn watcher, nil\n}\n"
  },
  {
    "path": "contrib/config/nacos/config_test.go",
    "content": "package config\n\nimport (\n\t\"reflect\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/nacos-group/nacos-sdk-go/clients\"\n\t\"github.com/nacos-group/nacos-sdk-go/common/constant\"\n\t\"github.com/nacos-group/nacos-sdk-go/vo\"\n\n\t\"github.com/go-kratos/kratos/v2/config\"\n)\n\nfunc TestConfig_Load(t *testing.T) {\n\tsc := []constant.ServerConfig{\n\t\t*constant.NewServerConfig(\"127.0.0.1\", 8848),\n\t}\n\n\tcc := constant.ClientConfig{\n\t\tTimeoutMs:           5000,\n\t\tNotLoadCacheAtStart: true,\n\t\tLogDir:              \"/tmp/nacos/log\",\n\t\tCacheDir:            \"/tmp/nacos/cache\",\n\t\tRotateTime:          \"1h\",\n\t\tMaxAge:              3,\n\t\tLogLevel:            \"debug\",\n\t}\n\n\tclient, err := clients.NewConfigClient(\n\t\tvo.NacosClientParam{\n\t\t\tClientConfig:  &cc,\n\t\t\tServerConfigs: sc,\n\t\t},\n\t)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tsource := NewConfigSource(client, WithGroup(\"test\"), WithDataID(\"test.yaml\"))\n\n\ttype fields struct {\n\t\tsource config.Source\n\t}\n\ttests := []struct {\n\t\tname      string\n\t\tfields    fields\n\t\twant      []*config.KeyValue\n\t\twantErr   bool\n\t\tpreFunc   func(t *testing.T)\n\t\tdeferFunc func(t *testing.T)\n\t}{\n\t\t{\n\t\t\tname: \"normal\",\n\t\t\tfields: fields{\n\t\t\t\tsource: source,\n\t\t\t},\n\t\t\twantErr: false,\n\t\t\tpreFunc: func(t *testing.T) {\n\t\t\t\t_, err = client.PublishConfig(vo.ConfigParam{DataId: \"test.yaml\", Group: \"test\", Content: \"test: test\"})\n\t\t\t\tif err != nil {\n\t\t\t\t\tt.Error(err)\n\t\t\t\t}\n\t\t\t\ttime.Sleep(time.Second * 1)\n\t\t\t},\n\t\t\tdeferFunc: func(t *testing.T) {\n\t\t\t\t_, dErr := client.DeleteConfig(vo.ConfigParam{DataId: \"test.yaml\", Group: \"test\"})\n\t\t\t\tif dErr != nil {\n\t\t\t\t\tt.Error(dErr)\n\t\t\t\t}\n\t\t\t},\n\t\t\twant: []*config.KeyValue{{\n\t\t\t\tKey:    \"test.yaml\",\n\t\t\t\tValue:  []byte(\"test: test\"),\n\t\t\t\tFormat: \"yaml\",\n\t\t\t}},\n\t\t},\n\t\t{\n\t\t\tname: \"error\",\n\t\t\tfields: fields{\n\t\t\t\tsource: source,\n\t\t\t},\n\t\t\twantErr: false,\n\t\t\tpreFunc: func(t *testing.T) {\n\t\t\t\t_, err = client.PublishConfig(vo.ConfigParam{DataId: \"111.yaml\", Group: \"notExist\", Content: \"test: test\"})\n\t\t\t\tif err != nil {\n\t\t\t\t\tt.Error(err)\n\t\t\t\t}\n\t\t\t\ttime.Sleep(time.Second * 1)\n\t\t\t},\n\t\t\tdeferFunc: func(t *testing.T) {\n\t\t\t\t_, dErr := client.DeleteConfig(vo.ConfigParam{DataId: \"111.yaml\", Group: \"notExist\"})\n\t\t\t\tif dErr != nil {\n\t\t\t\t\tt.Error(dErr)\n\t\t\t\t}\n\t\t\t},\n\t\t\twant: []*config.KeyValue{{\n\t\t\t\tKey:    \"test.yaml\",\n\t\t\t\tValue:  []byte{},\n\t\t\t\tFormat: \"yaml\",\n\t\t\t}},\n\t\t},\n\t}\n\tfor _, test := range tests {\n\t\tt.Run(test.name, func(t *testing.T) {\n\t\t\tif test.preFunc != nil {\n\t\t\t\ttest.preFunc(t)\n\t\t\t}\n\t\t\tif test.deferFunc != nil {\n\t\t\t\tdefer test.deferFunc(t)\n\t\t\t}\n\t\t\ts := test.fields.source\n\t\t\tconfigs, lErr := s.Load()\n\t\t\tif (lErr != nil) != test.wantErr {\n\t\t\t\tt.Errorf(\"Load error = %v, wantErr %v\", lErr, test.wantErr)\n\t\t\t\tt.Errorf(\"Load configs = %v\", configs)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif !reflect.DeepEqual(configs, test.want) {\n\t\t\t\tt.Errorf(\"Load configs = %v, want %v\", configs, test.want)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestConfig_Watch(t *testing.T) {\n\tsc := []constant.ServerConfig{\n\t\t*constant.NewServerConfig(\"127.0.0.1\", 8848),\n\t}\n\n\tcc := constant.ClientConfig{\n\t\tTimeoutMs:           5000,\n\t\tNotLoadCacheAtStart: true,\n\t\tLogDir:              \"/tmp/nacos/log\",\n\t\tCacheDir:            \"/tmp/nacos/cache\",\n\t\tRotateTime:          \"1h\",\n\t\tMaxAge:              3,\n\t\tLogLevel:            \"debug\",\n\t}\n\n\tclient, err := clients.NewConfigClient(\n\t\tvo.NacosClientParam{\n\t\t\tClientConfig:  &cc,\n\t\t\tServerConfigs: sc,\n\t\t},\n\t)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tsource := NewConfigSource(client, WithGroup(\"test\"), WithDataID(\"test.yaml\"))\n\n\ttype fields struct {\n\t\tsource config.Source\n\t}\n\ttests := []struct {\n\t\tname        string\n\t\tfields      fields\n\t\twant        []*config.KeyValue\n\t\twantErr     bool\n\t\tprocessFunc func(t *testing.T, w config.Watcher)\n\t\tdeferFunc   func(t *testing.T, w config.Watcher)\n\t}{\n\t\t{\n\t\t\tname: \"normal\",\n\t\t\tfields: fields{\n\t\t\t\tsource: source,\n\t\t\t},\n\t\t\twantErr: false,\n\t\t\tprocessFunc: func(t *testing.T, _ config.Watcher) {\n\t\t\t\t_, pErr := client.PublishConfig(vo.ConfigParam{DataId: \"test.yaml\", Group: \"test\", Content: \"test: test\"})\n\t\t\t\tif pErr != nil {\n\t\t\t\t\tt.Error(pErr)\n\t\t\t\t}\n\t\t\t},\n\t\t\tdeferFunc: func(t *testing.T, _ config.Watcher) {\n\t\t\t\t_, dErr := client.DeleteConfig(vo.ConfigParam{DataId: \"test.yaml\", Group: \"test\"})\n\t\t\t\tif dErr != nil {\n\t\t\t\t\tt.Error(dErr)\n\t\t\t\t}\n\t\t\t},\n\t\t\twant: []*config.KeyValue{{\n\t\t\t\tKey:    \"test.yaml\",\n\t\t\t\tValue:  []byte(\"test: test\"),\n\t\t\t\tFormat: \"yaml\",\n\t\t\t}},\n\t\t},\n\t}\n\tfor _, test := range tests {\n\t\tt.Run(test.name, func(t *testing.T) {\n\t\t\ts := test.fields.source\n\t\t\twatch, wErr := s.Watch()\n\t\t\tif wErr != nil {\n\t\t\t\tt.Error(wErr)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif test.processFunc != nil {\n\t\t\t\ttest.processFunc(t, watch)\n\t\t\t}\n\t\t\tif test.deferFunc != nil {\n\t\t\t\tdefer test.deferFunc(t, watch)\n\t\t\t}\n\t\t\twant, nErr := watch.Next()\n\t\t\tif (nErr != nil) != test.wantErr {\n\t\t\t\tt.Errorf(\"Watch error = %v, wantErr %v\", nErr, test.wantErr)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif !reflect.DeepEqual(want, test.want) {\n\t\t\t\tt.Errorf(\"Watch watcher = %v, want %v\", watch, test.want)\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "contrib/config/nacos/go.mod",
    "content": "module github.com/go-kratos/kratos/contrib/config/nacos/v2\n\ngo 1.22\n\nrequire (\n\tgithub.com/go-kratos/kratos/v2 v2.9.2\n\tgithub.com/nacos-group/nacos-sdk-go v1.0.9\n)\n\nrequire (\n\tdario.cat/mergo v1.0.0 // indirect\n\tgithub.com/aliyun/alibaba-cloud-sdk-go v1.61.18 // indirect\n\tgithub.com/buger/jsonparser v1.1.1 // indirect\n\tgithub.com/go-errors/errors v1.0.1 // indirect\n\tgithub.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af // indirect\n\tgithub.com/json-iterator/go v1.1.6 // indirect\n\tgithub.com/lestrrat/go-file-rotatelogs v0.0.0-20180223000712-d3151e2a480f // indirect\n\tgithub.com/lestrrat/go-strftime v0.0.0-20180220042222-ba3bf9c1d042 // indirect\n\tgithub.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect\n\tgithub.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 // indirect\n\tgithub.com/pkg/errors v0.9.1 // indirect\n\tgithub.com/toolkits/concurrent v0.0.0-20150624120057-a4371d70e3e3 // indirect\n\tgo.uber.org/atomic v1.6.0 // indirect\n\tgo.uber.org/multierr v1.5.0 // indirect\n\tgo.uber.org/zap v1.15.0 // indirect\n\tgoogle.golang.org/protobuf v1.33.0 // indirect\n\tgopkg.in/ini.v1 v1.42.0 // indirect\n\tgopkg.in/yaml.v3 v3.0.1 // indirect\n)\n\nreplace github.com/go-kratos/kratos/v2 => ../../../\n"
  },
  {
    "path": "contrib/config/nacos/go.sum",
    "content": "dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk=\ndario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=\ngithub.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=\ngithub.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=\ngithub.com/aliyun/alibaba-cloud-sdk-go v1.61.18 h1:zOVTBdCKFd9JbCKz9/nt+FovbjPFmb7mUnp8nH9fQBA=\ngithub.com/aliyun/alibaba-cloud-sdk-go v1.61.18/go.mod h1:v8ESoHo4SyHmuB4b1tJqDHxfTGEciD+yhvOU/5s1Rfk=\ngithub.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s=\ngithub.com/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMUs=\ngithub.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0=\ngithub.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.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/fastly/go-utils v0.0.0-20180712184237-d95a45783239 h1:Ghm4eQYC0nEPnSJdVkTrXpu9KtoVCSo1hg7mtI7G9KU=\ngithub.com/fastly/go-utils v0.0.0-20180712184237-d95a45783239/go.mod h1:Gdwt2ce0yfBxPvZrHkprdPPTTS3N5rwmLE8T22KBXlw=\ngithub.com/go-errors/errors v1.0.1 h1:LUHzmkK3GUKUrL/1gfBUxAHzcev3apQlezX/+O7ma6w=\ngithub.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=\ngithub.com/goji/httpauth v0.0.0-20160601135302-2da839ab0f4d/go.mod h1:nnjvkQ9ptGaCkuDUx6wNykzzlUixGxvkme+H/lnzb+A=\ngithub.com/golang/mock v1.3.1 h1:qGJ6qTW+x6xX/my+8YUVl4WNpX9B7+/l2tRsHGZ7f2s=\ngithub.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=\ngithub.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=\ngithub.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=\ngithub.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=\ngithub.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=\ngithub.com/jehiah/go-strftime v0.0.0-20171201141054-1d33003b3869 h1:IPJ3dvxmJ4uczJe5YQdrYB16oTJlGSC/OyZDqUk9xX4=\ngithub.com/jehiah/go-strftime v0.0.0-20171201141054-1d33003b3869/go.mod h1:cJ6Cj7dQo+O6GJNiMx+Pa94qKj+TG8ONdKHgMNIyyag=\ngithub.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af h1:pmfjZENx5imkbgOkpRUYLnmbU7UEFbjtDA2hxJ1ichM=\ngithub.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=\ngithub.com/jonboulle/clockwork v0.1.0 h1:VKV+ZcuP6l3yW9doeqz6ziZGgcynBVQO+obU0+0hcPo=\ngithub.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=\ngithub.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=\ngithub.com/json-iterator/go v1.1.6 h1:MrUvLMLTMxbqFJ9kzlvat/rYZqZnW3u4wkLzWTaFwKs=\ngithub.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=\ngithub.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=\ngithub.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=\ngithub.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=\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 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=\ngithub.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=\ngithub.com/lestrrat/go-envload v0.0.0-20180220120943-6ed08b54a570 h1:0iQektZGS248WXmGIYOwRXSQhD4qn3icjMpuxwO7qlo=\ngithub.com/lestrrat/go-envload v0.0.0-20180220120943-6ed08b54a570/go.mod h1:BLt8L9ld7wVsvEWQbuLrUZnCMnUmLZ+CGDzKtclrTlE=\ngithub.com/lestrrat/go-file-rotatelogs v0.0.0-20180223000712-d3151e2a480f h1:sgUSP4zdTUZYZgAGGtN5Lxk92rK+JUFOwf+FT99EEI4=\ngithub.com/lestrrat/go-file-rotatelogs v0.0.0-20180223000712-d3151e2a480f/go.mod h1:UGmTpUd3rjbtfIpwAPrcfmGf/Z1HS95TATB+m57TPB8=\ngithub.com/lestrrat/go-strftime v0.0.0-20180220042222-ba3bf9c1d042 h1:Bvq8AziQ5jFF4BHGAEDSqwPW1NJS3XshxbRCxtjFAZc=\ngithub.com/lestrrat/go-strftime v0.0.0-20180220042222-ba3bf9c1d042/go.mod h1:TPpsiPUEh0zFL1Snz4crhMlBe60PYxRHr5oFF3rRYg0=\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 h1:Esafd1046DLDQ0W1YjYsBW+p8U2u7vzgW2SQVmlNazg=\ngithub.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=\ngithub.com/nacos-group/nacos-sdk-go v1.0.9 h1:sMvrp6tZj4LdhuHRsS4GCqASB81k3pjmT2ykDQQpwt0=\ngithub.com/nacos-group/nacos-sdk-go v1.0.9/go.mod h1:hlAPn3UdzlxIlSILAyOXKxjFSvDJ9oLzTJ9hLAK1KzA=\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 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=\ngithub.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=\ngithub.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=\ngithub.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=\ngithub.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=\ngithub.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=\ngithub.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=\ngithub.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a h1:pa8hGb/2YqsZKovtsgrwcDH1RZhVbTKCjLp47XpqCDs=\ngithub.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=\ngithub.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\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.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=\ngithub.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=\ngithub.com/tebeka/strftime v0.1.3 h1:5HQXOqWKYRFfNyBMNVc9z5+QzuBtIXy03psIhtdJYto=\ngithub.com/tebeka/strftime v0.1.3/go.mod h1:7wJm3dZlpr4l/oVK0t1HYIc4rMzQ2XJlOMIUJUJH6XQ=\ngithub.com/toolkits/concurrent v0.0.0-20150624120057-a4371d70e3e3 h1:kF/7m/ZU+0D4Jj5eZ41Zm3IH/J8OElK1Qtd7tVKAwLk=\ngithub.com/toolkits/concurrent v0.0.0-20150624120057-a4371d70e3e3/go.mod h1:QDlpd3qS71vYtakd2hmdpqhJ9nwv6mD6A30bQ1BPBFE=\ngo.uber.org/atomic v1.6.0 h1:Ezj3JGmsOnG1MoRWQkPBsKLe9DwWD9QeXzTRzzldNVk=\ngo.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=\ngo.uber.org/multierr v1.5.0 h1:KCa4XfM8CWFCpxXRGok+Q0SS/0XBhMDbHHGABQLvD2A=\ngo.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU=\ngo.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee h1:0mgffUl7nfd+FpvXMVz4IDEaUSmT1ysygQC7qYo7sG4=\ngo.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=\ngo.uber.org/zap v1.15.0 h1:ZZCA22JRF2gQE5FoNmhmrf7jeJJ2uhqDUNRYKm8dvmM=\ngo.uber.org/zap v1.15.0/go.mod h1:Mb2vm2krFEG5DV0W9qcHBYFtp/Wku1cvYaqPsS/WYfc=\ngolang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=\ngolang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/lint v0.0.0-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA6JiaEZDtJb9Ei+1LlBs=\ngolang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=\ngolang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I=\ngolang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=\ngolang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=\ngolang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=\ngolang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=\ngolang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=\ngolang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=\ngolang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=\ngolang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5 h1:hKsoRgsbwY1NafxrwTs+k64bikrLBkAgPir1TNCj3Zs=\ngolang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=\ngolang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngoogle.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=\ngoogle.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=\ngopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/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/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=\ngopkg.in/ini.v1 v1.42.0 h1:7N3gPTt50s8GuLortA00n8AqRTk75qOP98+mTPpgzRk=\ngopkg.in/ini.v1 v1.42.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=\ngopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=\ngopkg.in/yaml.v2 v2.2.2/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=\nhonnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM=\nhonnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=\n"
  },
  {
    "path": "contrib/config/nacos/watcher.go",
    "content": "package config\n\nimport (\n\t\"context\"\n\t\"path/filepath\"\n\t\"strings\"\n\n\t\"github.com/nacos-group/nacos-sdk-go/vo\"\n\n\t\"github.com/go-kratos/kratos/v2/config\"\n)\n\ntype Watcher struct {\n\tdataID             string\n\tgroup              string\n\tcontent            chan string\n\tcancelListenConfig cancelListenConfigFunc\n\n\tctx    context.Context\n\tcancel context.CancelFunc\n}\n\ntype cancelListenConfigFunc func(params vo.ConfigParam) (err error)\n\nfunc newWatcher(ctx context.Context, dataID string, group string, cancelListenConfig cancelListenConfigFunc) *Watcher {\n\tctx, cancel := context.WithCancel(ctx)\n\tw := &Watcher{\n\t\tdataID:             dataID,\n\t\tgroup:              group,\n\t\tcancelListenConfig: cancelListenConfig,\n\t\tcontent:            make(chan string, 100),\n\n\t\tctx:    ctx,\n\t\tcancel: cancel,\n\t}\n\treturn w\n}\n\nfunc (w *Watcher) Next() ([]*config.KeyValue, error) {\n\tselect {\n\tcase <-w.ctx.Done():\n\t\treturn nil, w.ctx.Err()\n\tcase content := <-w.content:\n\t\tk := w.dataID\n\t\treturn []*config.KeyValue{\n\t\t\t{\n\t\t\t\tKey:    k,\n\t\t\t\tValue:  []byte(content),\n\t\t\t\tFormat: strings.TrimPrefix(filepath.Ext(k), \".\"),\n\t\t\t},\n\t\t}, nil\n\t}\n}\n\nfunc (w *Watcher) Close() error {\n\terr := w.cancelListenConfig(vo.ConfigParam{\n\t\tDataId: w.dataID,\n\t\tGroup:  w.group,\n\t})\n\tw.cancel()\n\treturn err\n}\n\nfunc (w *Watcher) Stop() error {\n\treturn w.Close()\n}\n"
  },
  {
    "path": "contrib/config/polaris/README.md",
    "content": "# Polaris Config\n\n```go\nimport (\n\t\"log\"\n\n\t\"github.com/polarismesh/polaris-go\"\n\n\t\"github.com/go-kratos/kratos/contrib/config/polaris/v2\"\n)\n\nfunc main() {\n    configApi, err := polaris.NewConfigAPI()\n    if err != nil {\n    \tlog.Fatalln(err)\n    }\n\n    source, err := New(&configApi, WithNamespace(\"default\"), WithFileGroup(\"default\"), WithFileName(\"default.yaml\"))\n    if err != nil {\n    \tlog.Fatalln(err)\n    }\n    source.Load()\n}\n```\n"
  },
  {
    "path": "contrib/config/polaris/config.go",
    "content": "package config\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"path/filepath\"\n\t\"strings\"\n\n\t\"github.com/polarismesh/polaris-go\"\n\n\t\"github.com/go-kratos/kratos/v2/config\"\n)\n\n// Option is polaris config option.\ntype Option func(o *options)\n\ntype options struct {\n\tnamespace  string\n\tfileGroup  string\n\tfileName   string\n\tconfigFile polaris.ConfigFile\n}\n\n// WithNamespace with polaris config namespace\nfunc WithNamespace(namespace string) Option {\n\treturn func(o *options) {\n\t\to.namespace = namespace\n\t}\n}\n\n// WithFileGroup with polaris config fileGroup\nfunc WithFileGroup(fileGroup string) Option {\n\treturn func(o *options) {\n\t\to.fileGroup = fileGroup\n\t}\n}\n\n// WithFileName with polaris config fileName\nfunc WithFileName(fileName string) Option {\n\treturn func(o *options) {\n\t\to.fileName = fileName\n\t}\n}\n\ntype source struct {\n\tclient  polaris.ConfigAPI\n\toptions *options\n}\n\nfunc New(client polaris.ConfigAPI, opts ...Option) (config.Source, error) {\n\toptions := &options{\n\t\tnamespace: \"default\",\n\t\tfileGroup: \"\",\n\t\tfileName:  \"\",\n\t}\n\n\tfor _, opt := range opts {\n\t\topt(options)\n\t}\n\n\tif options.fileGroup == \"\" {\n\t\treturn nil, errors.New(\"fileGroup invalid\")\n\t}\n\n\tif options.fileName == \"\" {\n\t\treturn nil, errors.New(\"fileName invalid\")\n\t}\n\n\treturn &source{\n\t\tclient:  client,\n\t\toptions: options,\n\t}, nil\n}\n\n// Load return the config values\nfunc (s *source) Load() ([]*config.KeyValue, error) {\n\tconfigFile, err := s.client.GetConfigFile(s.options.namespace, s.options.fileGroup, s.options.fileName)\n\tif err != nil {\n\t\tfmt.Println(\"fail to get config.\", err)\n\t\treturn nil, err\n\t}\n\n\tcontent := configFile.GetContent()\n\tk := s.options.fileName\n\n\ts.options.configFile = configFile\n\n\treturn []*config.KeyValue{\n\t\t{\n\t\t\tKey:    k,\n\t\t\tValue:  []byte(content),\n\t\t\tFormat: strings.TrimPrefix(filepath.Ext(k), \".\"),\n\t\t},\n\t}, nil\n}\n\n// Watch return the watcher\nfunc (s *source) Watch() (config.Watcher, error) {\n\treturn newWatcher(s.options.configFile), nil\n}\n"
  },
  {
    "path": "contrib/config/polaris/go.mod",
    "content": "module github.com/go-kratos/kratos/contrib/config/polaris/v2\n\ngo 1.22\n\nrequire (\n\tgithub.com/go-kratos/kratos/v2 v2.9.2\n\tgithub.com/polarismesh/polaris-go v1.1.0\n)\n\nrequire (\n\tdario.cat/mergo v1.0.0 // indirect\n\tgithub.com/beorn7/perks v1.0.1 // indirect\n\tgithub.com/cespare/xxhash/v2 v2.2.0 // indirect\n\tgithub.com/ghodss/yaml v1.0.0 // indirect\n\tgithub.com/golang/protobuf v1.5.4 // indirect\n\tgithub.com/google/uuid v1.4.0 // indirect\n\tgithub.com/hashicorp/errwrap v1.1.0 // indirect\n\tgithub.com/hashicorp/go-multierror v1.1.1 // indirect\n\tgithub.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect\n\tgithub.com/mitchellh/go-homedir v1.1.0 // indirect\n\tgithub.com/modern-go/reflect2 v1.0.2 // indirect\n\tgithub.com/natefinch/lumberjack v2.0.0+incompatible // indirect\n\tgithub.com/prometheus/client_golang v1.12.2 // indirect\n\tgithub.com/prometheus/client_model v0.2.0 // indirect\n\tgithub.com/prometheus/common v0.35.0 // indirect\n\tgithub.com/prometheus/procfs v0.7.3 // indirect\n\tgithub.com/spaolacci/murmur3 v1.1.0 // indirect\n\tgo.uber.org/atomic v1.9.0 // indirect\n\tgo.uber.org/multierr v1.8.0 // indirect\n\tgo.uber.org/zap v1.21.0 // indirect\n\tgolang.org/x/net v0.33.0 // indirect\n\tgolang.org/x/sys v0.28.0 // indirect\n\tgolang.org/x/text v0.21.0 // indirect\n\tgoogle.golang.org/genproto/googleapis/rpc v0.0.0-20240102182953-50ed04b92917 // indirect\n\tgoogle.golang.org/grpc v1.61.1 // indirect\n\tgoogle.golang.org/protobuf v1.33.0 // indirect\n\tgopkg.in/yaml.v2 v2.4.0 // indirect\n\tgopkg.in/yaml.v3 v3.0.1 // indirect\n)\n\nreplace github.com/go-kratos/kratos/v2 => ../../../\n"
  },
  {
    "path": "contrib/config/polaris/go.sum",
    "content": "cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=\ncloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=\ncloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=\ncloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=\ncloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=\ncloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=\ncloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=\ncloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To=\ncloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4=\ncloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M=\ncloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc=\ncloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk=\ncloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs=\ncloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc=\ncloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY=\ncloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=\ncloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=\ncloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=\ncloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=\ncloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=\ncloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=\ncloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=\ncloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=\ncloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=\ncloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=\ncloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=\ncloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU=\ncloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=\ncloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=\ncloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=\ncloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=\ncloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=\ndario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk=\ndario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=\ndmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=\ngithub.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=\ngithub.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=\ngithub.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=\ngithub.com/agiledragon/gomonkey v2.0.2+incompatible h1:eXKi9/piiC3cjJD1658mEE2o3NjkJ5vDLgYjCQu0Xlw=\ngithub.com/agiledragon/gomonkey v2.0.2+incompatible/go.mod h1:2NGfXu1a80LLr2cmWXGBDaHEjb1idR6+FVlX5T3D9hw=\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/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=\ngithub.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=\ngithub.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=\ngithub.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=\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 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=\ngithub.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=\ngithub.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=\ngithub.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=\ngithub.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=\ngithub.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=\ngithub.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=\ngithub.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=\ngithub.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=\ngithub.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=\ngithub.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=\ngithub.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=\ngithub.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=\ngithub.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI=\ngithub.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=\ngithub.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=\ngithub.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=\ngithub.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.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/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=\ngithub.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=\ngithub.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=\ngithub.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=\ngithub.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0=\ngithub.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=\ngithub.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=\ngithub.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=\ngithub.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=\ngithub.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=\ngithub.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=\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-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=\ngithub.com/go-kit/log v0.2.0/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0=\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-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=\ngithub.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=\ngithub.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=\ngithub.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=\ngithub.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=\ngithub.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=\ngithub.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=\ngithub.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=\ngithub.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=\ngithub.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=\ngithub.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=\ngithub.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=\ngithub.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=\ngithub.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=\ngithub.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=\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.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=\ngithub.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=\ngithub.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=\ngithub.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=\ngithub.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=\ngithub.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=\ngithub.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=\ngithub.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=\ngithub.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=\ngithub.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=\ngithub.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=\ngithub.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=\ngithub.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=\ngithub.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=\ngithub.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=\ngithub.com/gonum/blas v0.0.0-20181208220705-f22b278b28ac/go.mod h1:P32wAyui1PQ58Oce/KYkOqQv8cVw1zAapXOl+dRFGbc=\ngithub.com/gonum/floats v0.0.0-20181209220543-c233463c7e82/go.mod h1:PxC8OnwL11+aosOB5+iEPoV3picfs8tUpkVd0pDo+Kg=\ngithub.com/gonum/integrate v0.0.0-20181209220457-a422b5c0fdf2/go.mod h1:pDgmNM6seYpwvPos3q+zxlXMsbve6mOIPucUnUOrI7Y=\ngithub.com/gonum/internal v0.0.0-20181124074243-f884aa714029/go.mod h1:Pu4dmpkhSyOzRwuXkOgAvijx4o+4YMUJJo9OvPYMkks=\ngithub.com/gonum/lapack v0.0.0-20181123203213-e4cdc5a0bff9/go.mod h1:XA3DeT6rxh2EAE789SSiSJNqxPaC0aE9J8NTOI0Jo/A=\ngithub.com/gonum/matrix v0.0.0-20181209220409-c518dec07be9/go.mod h1:0EXg4mc1CNP0HCqCz+K4ts155PXIlUywf0wqN+GfPZw=\ngithub.com/gonum/stat v0.0.0-20181125101827-41a0da705a5b/go.mod h1:Z4GIJBJO3Wa4gD4vbwQxXXZ+WHmW6E9ixmNrwvs0iZs=\ngithub.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=\ngithub.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=\ngithub.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=\ngithub.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=\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.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\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/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=\ngithub.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=\ngithub.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=\ngithub.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=\ngithub.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=\ngithub.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=\ngithub.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=\ngithub.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=\ngithub.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=\ngithub.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=\ngithub.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=\ngithub.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=\ngithub.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=\ngithub.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4=\ngithub.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=\ngithub.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=\ngithub.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=\ngithub.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=\ngithub.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=\ngithub.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=\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-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/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=\ngithub.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=\ngithub.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=\ngithub.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=\ngithub.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=\ngithub.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=\ngithub.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=\ngithub.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=\ngithub.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=\ngithub.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=\ngithub.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=\ngithub.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=\ngithub.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=\ngithub.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=\ngithub.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=\ngithub.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=\ngithub.com/konsorten/go-windows-terminal-sequences v1.0.3/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 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=\ngithub.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=\ngithub.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=\ngithub.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=\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/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=\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/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=\ngithub.com/natefinch/lumberjack v2.0.0+incompatible h1:4QJd3OLAMgj7ph+yZTuX13Ld4UpgHp07nNdFX7mqFfM=\ngithub.com/natefinch/lumberjack v2.0.0+incompatible/go.mod h1:Wi9p2TTF5DG5oU+6YfsmYQpsTIOm0B1VNzQg9Mw6nPk=\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 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=\ngithub.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=\ngithub.com/polarismesh/polaris-go v1.1.0 h1:nFvn3q3XaVFhzF7pBnIySrN0ZZBwvbbYXC5r2DpsQN0=\ngithub.com/polarismesh/polaris-go v1.1.0/go.mod h1:tquawfjEKp1W3ffNJQSzhfditjjoZ7tvhOCElN7Efzs=\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.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=\ngithub.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0=\ngithub.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY=\ngithub.com/prometheus/client_golang v1.12.2 h1:51L9cDoUHVrXx4zWYlcLQIZ+d+VXHgqnYKkIuq4g/34=\ngithub.com/prometheus/client_golang v1.12.2/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY=\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.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=\ngithub.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M=\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.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=\ngithub.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc=\ngithub.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls=\ngithub.com/prometheus/common v0.35.0 h1:Eyr+Pw2VymWejHqCugNaQXkAi6KayVNxaHeu6khmFBE=\ngithub.com/prometheus/common v0.35.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA=\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.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=\ngithub.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=\ngithub.com/prometheus/procfs v0.7.3 h1:4jVXhlkAyzOScmCkXBTOLRLTz8EeU+eyjrwB/EPq0VU=\ngithub.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=\ngithub.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=\ngithub.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=\ngithub.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=\ngithub.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=\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/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=\ngithub.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=\ngithub.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=\ngithub.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=\ngithub.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=\ngithub.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI=\ngithub.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=\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/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.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=\ngithub.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=\ngithub.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngithub.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=\ngithub.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=\ngithub.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=\ngithub.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=\ngo.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=\ngo.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=\ngo.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=\ngo.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=\ngo.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=\ngo.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=\ngo.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=\ngo.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE=\ngo.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=\ngo.uber.org/goleak v1.1.11-0.20210813005559-691160354723/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=\ngo.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI=\ngo.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=\ngo.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=\ngo.uber.org/multierr v1.8.0 h1:dg6GjLku4EH+249NNmoIciG9N/jURbDG+pFlTkhzIC8=\ngo.uber.org/multierr v1.8.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak=\ngo.uber.org/zap v1.19.1/go.mod h1:j3DNczoxDZroyBnOT1L/Q79cfUMGZxlv/9dzN7SM1rI=\ngo.uber.org/zap v1.21.0 h1:WefMeulhovoZ2sYXz7st6K0sLj7bBhpiFaud4r4zST8=\ngo.uber.org/zap v1.21.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw=\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-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\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/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=\ngolang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=\ngolang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=\ngolang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=\ngolang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=\ngolang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=\ngolang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=\ngolang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=\ngolang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=\ngolang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=\ngolang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=\ngolang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=\ngolang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=\ngolang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=\ngolang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=\ngolang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=\ngolang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=\ngolang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=\ngolang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=\ngolang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=\ngolang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=\ngolang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=\ngolang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=\ngolang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=\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/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=\ngolang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=\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-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20200222125558-5a598a2470a0/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-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=\ngolang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=\ngolang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=\ngolang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=\ngolang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=\ngolang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=\ngolang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=\ngolang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=\ngolang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=\ngolang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=\ngolang.org/x/net v0.0.0-20210917221730-978cfadd31cf/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=\ngolang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=\ngolang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=\ngolang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I=\ngolang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=\ngolang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=\ngolang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=\ngolang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=\ngolang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=\ngolang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=\ngolang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=\ngolang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc=\ngolang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\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-20190227155943-e225da77a7e6/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-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20201207232520-09787c993a3a/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.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=\ngolang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=\ngolang.org/x/sys v0.0.0-20180830151530-49385e6e1522/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-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\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-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=\ngolang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=\ngolang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=\ngolang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=\ngolang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/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.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=\ngolang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=\ngolang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=\ngolang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=\ngolang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=\ngolang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=\ngolang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=\ngolang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=\ngolang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=\ngolang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=\ngolang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=\ngolang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=\ngolang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=\ngolang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=\ngolang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=\ngolang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=\ngolang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=\ngolang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/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-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=\ngolang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=\ngolang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=\ngolang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=\ngolang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=\ngolang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=\ngolang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=\ngolang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=\ngolang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=\ngolang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=\ngolang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=\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=\ngoogle.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=\ngoogle.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=\ngoogle.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=\ngoogle.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=\ngoogle.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=\ngoogle.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=\ngoogle.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=\ngoogle.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=\ngoogle.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=\ngoogle.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=\ngoogle.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=\ngoogle.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=\ngoogle.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=\ngoogle.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=\ngoogle.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM=\ngoogle.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc=\ngoogle.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=\ngoogle.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=\ngoogle.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=\ngoogle.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=\ngoogle.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=\ngoogle.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=\ngoogle.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=\ngoogle.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=\ngoogle.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=\ngoogle.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=\ngoogle.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=\ngoogle.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=\ngoogle.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=\ngoogle.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=\ngoogle.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=\ngoogle.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=\ngoogle.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=\ngoogle.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=\ngoogle.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=\ngoogle.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=\ngoogle.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA=\ngoogle.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U=\ngoogle.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=\ngoogle.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=\ngoogle.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=\ngoogle.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=\ngoogle.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=\ngoogle.golang.org/genproto/googleapis/rpc v0.0.0-20240102182953-50ed04b92917 h1:6G8oQ016D88m1xAKljMlBOOGWDZkes4kMhgGFlf8WcQ=\ngoogle.golang.org/genproto/googleapis/rpc v0.0.0-20240102182953-50ed04b92917/go.mod h1:xtjpI3tXFPP051KaWnhvxkiubL/6dJ18vLVf7q2pTOU=\ngoogle.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=\ngoogle.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=\ngoogle.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=\ngoogle.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=\ngoogle.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=\ngoogle.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=\ngoogle.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=\ngoogle.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=\ngoogle.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60=\ngoogle.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=\ngoogle.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=\ngoogle.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=\ngoogle.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=\ngoogle.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=\ngoogle.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=\ngoogle.golang.org/grpc v1.61.1 h1:kLAiWrZs7YeDM6MumDe7m3y4aM6wacLzM1Y/wiLP9XY=\ngoogle.golang.org/grpc v1.61.1/go.mod h1:VUbo7IFqmF1QtCAstipjG0GIoq49KvMe9+h1jFLBNJs=\ngoogle.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=\ngoogle.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=\ngoogle.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=\ngoogle.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=\ngoogle.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=\ngoogle.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=\ngoogle.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=\ngoogle.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=\ngoogle.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=\ngoogle.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=\ngoogle.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=\ngoogle.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=\ngoogle.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=\ngoogle.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=\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-20180628173108-788fd7840127/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/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=\ngopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8=\ngopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=\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.3/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.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=\ngopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=\ngopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\ngopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\ngopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=\ngopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\nhonnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\nhonnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\nhonnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\nhonnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\nhonnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=\nhonnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=\nhonnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=\nrsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=\nrsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=\nrsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=\n"
  },
  {
    "path": "contrib/config/polaris/polaris.yaml",
    "content": "global:\n  serverConnector:\n    addresses:\n      - 127.0.0.1:8091\nconfig:\n  configConnector:\n    addresses:\n      - 127.0.0.1:8093\n"
  },
  {
    "path": "contrib/config/polaris/watcher.go",
    "content": "package config\n\nimport (\n\t\"fmt\"\n\t\"path/filepath\"\n\t\"strings\"\n\n\t\"github.com/polarismesh/polaris-go\"\n\t\"github.com/polarismesh/polaris-go/pkg/model\"\n\n\t\"github.com/go-kratos/kratos/v2/config\"\n\t\"github.com/go-kratos/kratos/v2/log\"\n)\n\ntype Watcher struct {\n\tconfigFile polaris.ConfigFile\n\tfullPath   string\n}\n\ntype eventChan struct {\n\tclosed bool\n\tevent  chan model.ConfigFileChangeEvent\n}\n\nvar eventChanMap = make(map[string]eventChan)\n\nfunc getFullPath(namespace string, fileGroup string, fileName string) string {\n\treturn fmt.Sprintf(\"%s/%s/%s\", namespace, fileGroup, fileName)\n}\n\nfunc receive(event model.ConfigFileChangeEvent) {\n\tmeta := event.ConfigFileMetadata\n\tec := eventChanMap[getFullPath(meta.GetNamespace(), meta.GetFileGroup(), meta.GetFileName())]\n\tdefer func() {\n\t\tif err := recover(); err != nil {\n\t\t\tlog.Error(err)\n\t\t}\n\t}()\n\tif !ec.closed {\n\t\tec.event <- event\n\t}\n}\n\nfunc newWatcher(configFile polaris.ConfigFile) *Watcher {\n\tconfigFile.AddChangeListener(receive)\n\n\tfullPath := getFullPath(configFile.GetNamespace(), configFile.GetFileGroup(), configFile.GetFileName())\n\tif _, ok := eventChanMap[fullPath]; !ok {\n\t\teventChanMap[fullPath] = eventChan{\n\t\t\tclosed: false,\n\t\t\tevent:  make(chan model.ConfigFileChangeEvent),\n\t\t}\n\t}\n\tw := &Watcher{\n\t\tconfigFile: configFile,\n\t\tfullPath:   fullPath,\n\t}\n\treturn w\n}\n\nfunc (w *Watcher) Next() ([]*config.KeyValue, error) {\n\tec := eventChanMap[w.fullPath]\n\tevent := <-ec.event\n\treturn []*config.KeyValue{\n\t\t{\n\t\t\tKey:    w.configFile.GetFileName(),\n\t\t\tValue:  []byte(event.NewValue),\n\t\t\tFormat: strings.TrimPrefix(filepath.Ext(w.configFile.GetFileName()), \".\"),\n\t\t},\n\t}, nil\n}\n\nfunc (w *Watcher) Stop() error {\n\tec := eventChanMap[w.fullPath]\n\tif !ec.closed {\n\t\tec.closed = true\n\t\tclose(ec.event)\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "contrib/encoding/msgpack/go.mod",
    "content": "module github.com/go-kratos/kratos/contrib/encoding/msgpack/v2\n\ngo 1.22\n\nrequire (\n\tgithub.com/go-kratos/kratos/v2 v2.9.2\n\tgithub.com/vmihailenco/msgpack/v5 v5.4.1\n)\n\nrequire github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect\n\nreplace github.com/go-kratos/kratos/v2 => ../../../\n"
  },
  {
    "path": "contrib/encoding/msgpack/go.sum",
    "content": "github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=\ngithub.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\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/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=\ngithub.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngithub.com/vmihailenco/msgpack/v5 v5.4.1 h1:cQriyiUvjTwOHg8QZaPihLWeRAAVoCpE00IUPn0Bjt8=\ngithub.com/vmihailenco/msgpack/v5 v5.4.1/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21PIudVV/E3rRQok=\ngithub.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g=\ngithub.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds=\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": "contrib/encoding/msgpack/msgpack.go",
    "content": "package msgpack\n\nimport (\n\t\"github.com/vmihailenco/msgpack/v5\"\n\n\t\"github.com/go-kratos/kratos/v2/encoding\"\n)\n\n// Name is the name registered for the msgpack compressor.\nconst Name = \"msgpack\"\n\nfunc init() {\n\tencoding.RegisterCodec(codec{})\n}\n\n// codec is a Codec implementation with msgpack.\ntype codec struct{}\n\nfunc (codec) Marshal(v any) ([]byte, error) {\n\treturn msgpack.Marshal(v)\n}\n\nfunc (codec) Unmarshal(data []byte, v any) error {\n\treturn msgpack.Unmarshal(data, v)\n}\n\nfunc (codec) Name() string {\n\treturn Name\n}\n"
  },
  {
    "path": "contrib/encoding/msgpack/msgpack_test.go",
    "content": "package msgpack\n\nimport (\n\t\"reflect\"\n\t\"testing\"\n)\n\ntype loginRequest struct {\n\tUserName string\n\tPassword string\n}\n\ntype testModel struct {\n\tID   int32\n\tName string\n}\n\nfunc TestName(t *testing.T) {\n\tc := new(codec)\n\tif !reflect.DeepEqual(\"msgpack\", c.Name()) {\n\t\tt.Errorf(\"Name() should be msgpack, but got %s\", c.Name())\n\t}\n}\n\nfunc TestCodec(t *testing.T) {\n\tc := new(codec)\n\tt2 := testModel{ID: 1, Name: \"name\"}\n\tm, err := c.Marshal(&t2)\n\tif err != nil {\n\t\tt.Errorf(\"Marshal() should be nil, but got %s\", err)\n\t}\n\tvar t3 testModel\n\terr = c.Unmarshal(m, &t3)\n\tif err != nil {\n\t\tt.Errorf(\"Unmarshal() should be nil, but got %s\", err)\n\t}\n\tif !reflect.DeepEqual(t2.ID, t3.ID) {\n\t\tt.Errorf(\"ID should be %d, but got %d\", t2.ID, t3.ID)\n\t}\n\tif !reflect.DeepEqual(t3.Name, t2.Name) {\n\t\tt.Errorf(\"Name should be %s, but got %s\", t2.Name, t3.Name)\n\t}\n\n\trequest := loginRequest{\n\t\tUserName: \"username\",\n\t\tPassword: \"password\",\n\t}\n\tm, err = c.Marshal(&request)\n\tif err != nil {\n\t\tt.Errorf(\"Marshal() should be nil, but got %s\", err)\n\t}\n\tvar req loginRequest\n\terr = c.Unmarshal(m, &req)\n\tif err != nil {\n\t\tt.Errorf(\"Unmarshal() should be nil, but got %s\", err)\n\t}\n\tif !reflect.DeepEqual(req.Password, request.Password) {\n\t\tt.Errorf(\"ID should be %s, but got %s\", req.Password, request.Password)\n\t}\n\tif !reflect.DeepEqual(req.UserName, request.UserName) {\n\t\tt.Errorf(\"Name should be %s, but got %s\", req.UserName, request.UserName)\n\t}\n}\n"
  },
  {
    "path": "contrib/errortracker/sentry/README.md",
    "content": "# Sentry middleware for Kratos\nThis middleware helps you to catch panics and report them to [sentry](https://sentry.io/)\n\n## Quick Start\nYou could check the full demo in example folder.\n```go\n// Step 1:\n// init sentry in the entry of your application\nimport \"github.com/getsentry/sentry-go\"\n\nsentry.Init(sentry.ClientOptions{\n\tDsn: \"<your dsn>\",\n\tAttachStacktrace: true, // recommended\n})\n\n// Step 2:\n// set middleware\nimport ksentry \"github.com/go-kratos/kratos/contrib/errortracker/sentry/v2\"\n\n// for HTTP server, new HTTP server with sentry middleware options\nvar opts = []http.ServerOption{\n\thttp.Middleware(\n\t\trecovery.Recovery(),\n\t\ttracing.Server(),\n\t\tksentry.Server(ksentry.WithTags(map[string]interface{}{\n\t\t\t\"tag\": \"some-custom-constant-tag\",\n\t\t\t\"trace_id\": tracing.TraceID(), // If you want to use the TraceID valuer, you need to place it after the A middleware.\n\t\t})), // must after Recovery middleware, because of the exiting order will be reversed\n\t\tlogging.Server(logger),\n\t),\n}\n\n// for gRPC server, new gRPC server with sentry middleware options\nvar opts = []grpc.ServerOption{\n\tgrpc.Middleware(\n\t\trecovery.Recovery(),\n\t\ttracing.Server(),\n\t\tksentry.Server(ksentry.WithTags(map[string]interface{}{\n\t\t\t\"tag\": \"some-custom-constant-tag\",\n\t\t\t\"trace_id\": tracing.TraceID(), // If you want to use the TraceID valuer, you need to place it after the A middleware.\n\t\t})), // must after Recovery middleware, because of the exiting order will be reversed\n\t\tlogging.Server(logger),\n\t),\n}\n\n// Then, the framework will report events to Sentry when your trigger panics.\n// Or your can push events to Sentry manually\n```\n\n## Reference\n* [https://docs.sentry.io/platforms/go/](https://docs.sentry.io/platforms/go/)\n"
  },
  {
    "path": "contrib/errortracker/sentry/go.mod",
    "content": "module github.com/go-kratos/kratos/contrib/errortracker/sentry/v2\n\ngo 1.22\n\nrequire (\n\tgithub.com/getsentry/sentry-go v0.25.0\n\tgithub.com/go-kratos/kratos/v2 v2.9.2\n)\n\nrequire (\n\tgithub.com/go-kratos/aegis v0.2.0 // indirect\n\tgithub.com/go-playground/form/v4 v4.2.0 // indirect\n\tgithub.com/golang/protobuf v1.5.4 // indirect\n\tgithub.com/google/uuid v1.4.0 // indirect\n\tgithub.com/gorilla/mux v1.8.1 // indirect\n\tgithub.com/kr/text v0.2.0 // indirect\n\tgolang.org/x/net v0.33.0 // indirect\n\tgolang.org/x/sys v0.28.0 // indirect\n\tgolang.org/x/text v0.21.0 // indirect\n\tgoogle.golang.org/genproto v0.0.0-20231212172506-995d672761c0 // indirect\n\tgoogle.golang.org/genproto/googleapis/api v0.0.0-20240102182953-50ed04b92917 // indirect\n\tgoogle.golang.org/genproto/googleapis/rpc v0.0.0-20240102182953-50ed04b92917 // indirect\n\tgoogle.golang.org/grpc v1.61.1 // indirect\n\tgoogle.golang.org/protobuf v1.33.0 // indirect\n\tgopkg.in/yaml.v3 v3.0.1 // indirect\n)\n\nreplace github.com/go-kratos/kratos/v2 => ../../../\n"
  },
  {
    "path": "contrib/errortracker/sentry/go.sum",
    "content": "github.com/census-instrumentation/opencensus-proto v0.4.1 h1:iKLQ0xPNFxR/2hzXZMrBo8f1j86j5WHzznCCQxV/b8g=\ngithub.com/census-instrumentation/opencensus-proto v0.4.1/go.mod h1:4T9NM4+4Vw91VeyqjLS6ao50K5bOcLKN6Q42XnYaRYw=\ngithub.com/cncf/xds/go v0.0.0-20231109132714-523115ebc101 h1:7To3pQ+pZo0i3dsWEbinPNFs5gPSBOsJtx3wTT94VBY=\ngithub.com/cncf/xds/go v0.0.0-20231109132714-523115ebc101/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=\ngithub.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=\ngithub.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/envoyproxy/go-control-plane v0.11.2-0.20230627204322-7d0032219fcb h1:kxNVXsNro/lpR5WD+P1FI/yUHn2G03Glber3k8cQL2Y=\ngithub.com/envoyproxy/go-control-plane v0.11.2-0.20230627204322-7d0032219fcb/go.mod h1:GxGqnjWzl1Gz8WfAfMJSfhvsi4EPZayRb25nLHDWXyA=\ngithub.com/envoyproxy/protoc-gen-validate v1.0.2 h1:QkIBuU5k+x7/QXPvPPnWXWlCdaBFApVqftFV6k087DA=\ngithub.com/envoyproxy/protoc-gen-validate v1.0.2/go.mod h1:GpiZQP3dDbg4JouG/NNS7QWXpgx6x8QiMKdmN72jogE=\ngithub.com/getsentry/sentry-go v0.25.0 h1:q6Eo+hS+yoJlTO3uu/azhQadsD8V+jQn2D8VvX1eOyI=\ngithub.com/getsentry/sentry-go v0.25.0/go.mod h1:lc76E2QywIyW8WuBnwl8Lc4bkmQH4+w1gwTf25trprY=\ngithub.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA=\ngithub.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og=\ngithub.com/go-kratos/aegis v0.2.0 h1:dObzCDWn3XVjUkgxyBp6ZeWtx/do0DPZ7LY3yNSJLUQ=\ngithub.com/go-kratos/aegis v0.2.0/go.mod h1:v0R2m73WgEEYB3XYu6aE2WcMwsZkJ/Rzuf5eVccm7bI=\ngithub.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A=\ngithub.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=\ngithub.com/go-playground/form/v4 v4.2.0 h1:N1wh+Goz61e6w66vo8vJkQt+uwZSoLz50kZPJWR8eic=\ngithub.com/go-playground/form/v4 v4.2.0/go.mod h1:q1a2BY+AQUUzhl6xA/6hBetay6dEIhMHjgvJiGo6K7U=\ngithub.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=\ngithub.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=\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/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4=\ngithub.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=\ngithub.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=\ngithub.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=\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/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4=\ngithub.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8=\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 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=\ngithub.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=\ngithub.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=\ngithub.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=\ngithub.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=\ngithub.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=\ngolang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I=\ngolang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=\ngolang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=\ngolang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=\ngolang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=\ngolang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=\ngolang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=\ngolang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=\ngoogle.golang.org/genproto v0.0.0-20231212172506-995d672761c0 h1:YJ5pD9rF8o9Qtta0Cmy9rdBwkSjrTCT6XTiUQVOtIos=\ngoogle.golang.org/genproto v0.0.0-20231212172506-995d672761c0/go.mod h1:l/k7rMz0vFTBPy+tFSGvXEd3z+BcoG1k7EHbqm+YBsY=\ngoogle.golang.org/genproto/googleapis/api v0.0.0-20240102182953-50ed04b92917 h1:rcS6EyEaoCO52hQDupoSfrxI3R6C2Tq741is7X8OvnM=\ngoogle.golang.org/genproto/googleapis/api v0.0.0-20240102182953-50ed04b92917/go.mod h1:CmlNWB9lSezaYELKS5Ym1r44VrrbPUa7JTvw+6MbpJ0=\ngoogle.golang.org/genproto/googleapis/rpc v0.0.0-20240102182953-50ed04b92917 h1:6G8oQ016D88m1xAKljMlBOOGWDZkes4kMhgGFlf8WcQ=\ngoogle.golang.org/genproto/googleapis/rpc v0.0.0-20240102182953-50ed04b92917/go.mod h1:xtjpI3tXFPP051KaWnhvxkiubL/6dJ18vLVf7q2pTOU=\ngoogle.golang.org/grpc v1.61.1 h1:kLAiWrZs7YeDM6MumDe7m3y4aM6wacLzM1Y/wiLP9XY=\ngoogle.golang.org/grpc v1.61.1/go.mod h1:VUbo7IFqmF1QtCAstipjG0GIoq49KvMe9+h1jFLBNJs=\ngoogle.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=\ngoogle.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=\ngopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/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.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=\ngopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\n"
  },
  {
    "path": "contrib/errortracker/sentry/sentry.go",
    "content": "package sentry\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"net\"\n\t\"os\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/getsentry/sentry-go\"\n\n\t\"github.com/go-kratos/kratos/v2/log\"\n\t\"github.com/go-kratos/kratos/v2/middleware\"\n\t\"github.com/go-kratos/kratos/v2/transport\"\n\t\"github.com/go-kratos/kratos/v2/transport/grpc\"\n\t\"github.com/go-kratos/kratos/v2/transport/http\"\n)\n\ntype ctxKey struct{}\n\ntype Option func(*options)\n\ntype options struct {\n\trepanic         bool\n\twaitForDelivery bool\n\ttimeout         time.Duration\n\ttags            map[string]any\n}\n\n// WithRepanic repanic configures whether Sentry should repanic after recovery, in most cases it should be set to true.\nfunc WithRepanic(repanic bool) Option {\n\treturn func(opts *options) {\n\t\topts.repanic = repanic\n\t}\n}\n\n// WithWaitForDelivery waitForDelivery configures whether you want to block the request before moving forward with the response.\nfunc WithWaitForDelivery(waitForDelivery bool) Option {\n\treturn func(opts *options) {\n\t\topts.waitForDelivery = waitForDelivery\n\t}\n}\n\n// WithTimeout timeout for the event delivery requests.\nfunc WithTimeout(timeout time.Duration) Option {\n\treturn func(opts *options) {\n\t\topts.timeout = timeout\n\t}\n}\n\n// WithTags global tags injection, the value type must be string or log.Valuer\nfunc WithTags(kvs map[string]any) Option {\n\treturn func(opts *options) {\n\t\topts.tags = kvs\n\t}\n}\n\n// Server returns a new server middleware for Sentry.\nfunc Server(opts ...Option) middleware.Middleware {\n\tconf := options{repanic: true}\n\tfor _, o := range opts {\n\t\to(&conf)\n\t}\n\tif conf.timeout == 0 {\n\t\tconf.timeout = 2 * time.Second\n\t}\n\treturn func(handler middleware.Handler) middleware.Handler {\n\t\treturn func(ctx context.Context, req any) (reply any, err error) {\n\t\t\thub := GetHubFromContext(ctx)\n\t\t\tscope := hub.Scope()\n\n\t\t\tfor k, v := range conf.tags {\n\t\t\t\tswitch val := v.(type) {\n\t\t\t\tcase string:\n\t\t\t\t\tscope.SetTag(k, val)\n\t\t\t\tcase log.Valuer:\n\t\t\t\t\tscope.SetTag(k, fmt.Sprintf(\"%v\", val(ctx)))\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif tr, ok := transport.FromServerContext(ctx); ok {\n\t\t\t\tswitch tr.Kind() {\n\t\t\t\tcase transport.KindGRPC:\n\t\t\t\t\tgtr := tr.(*grpc.Transport)\n\t\t\t\t\tscope.SetContext(\"gRPC\", map[string]any{\n\t\t\t\t\t\t\"endpoint\":  gtr.Endpoint(),\n\t\t\t\t\t\t\"operation\": gtr.Operation(),\n\t\t\t\t\t})\n\t\t\t\t\theaders := make(map[string]any)\n\t\t\t\t\tfor _, k := range gtr.RequestHeader().Keys() {\n\t\t\t\t\t\theaders[k] = gtr.RequestHeader().Get(k)\n\t\t\t\t\t}\n\t\t\t\t\tscope.SetContext(\"Headers\", headers)\n\t\t\t\tcase transport.KindHTTP:\n\t\t\t\t\thtr := tr.(*http.Transport)\n\t\t\t\t\tr := htr.Request()\n\t\t\t\t\tscope.SetRequest(r)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tctx = context.WithValue(ctx, ctxKey{}, hub)\n\t\t\tdefer recoverWithSentry(ctx, conf, hub, req)\n\t\t\treturn handler(ctx, req)\n\t\t}\n\t}\n}\n\nfunc recoverWithSentry(ctx context.Context, opts options, hub *sentry.Hub, req any) {\n\tif err := recover(); err != nil {\n\t\tif !isBrokenPipeError(err) {\n\t\t\teventID := hub.RecoverWithContext(\n\t\t\t\tcontext.WithValue(ctx, sentry.RequestContextKey, req),\n\t\t\t\terr,\n\t\t\t)\n\t\t\tif eventID != nil && opts.waitForDelivery {\n\t\t\t\thub.Flush(opts.timeout)\n\t\t\t}\n\t\t}\n\t\tif opts.repanic {\n\t\t\tpanic(err)\n\t\t}\n\t}\n}\n\nfunc isBrokenPipeError(err any) bool {\n\tif netErr, ok := err.(*net.OpError); ok {\n\t\tif sysErr, ok := netErr.Err.(*os.SyscallError); ok {\n\t\t\tif strings.Contains(strings.ToLower(sysErr.Error()), \"broken pipe\") ||\n\t\t\t\tstrings.Contains(strings.ToLower(sysErr.Error()), \"connection reset by peer\") {\n\t\t\t\treturn true\n\t\t\t}\n\t\t}\n\t}\n\treturn false\n}\n\n// GetHubFromContext retrieves attached *sentry.Hub instance from context or sentry.\n// You can use this hub for extra information reporting\nfunc GetHubFromContext(ctx context.Context) *sentry.Hub {\n\tif hub, ok := ctx.Value(ctxKey{}).(*sentry.Hub); ok {\n\t\treturn hub\n\t}\n\treturn sentry.CurrentHub().Clone()\n}\n"
  },
  {
    "path": "contrib/errortracker/sentry/sentry_test.go",
    "content": "package sentry\n\nimport (\n\t\"testing\"\n\t\"time\"\n)\n\nfunc TestWithTags(t *testing.T) {\n\topts := new(options)\n\tstrval := \"bar\"\n\tkvs := map[string]any{\n\t\t\"foo\": strval,\n\t}\n\tfuncTags := WithTags(kvs)\n\tfuncTags(opts)\n\tif opts.tags[\"foo\"].(string) != strval {\n\t\tt.Errorf(\"TestWithTags() = %v, want %v\", opts.tags[\"foo\"].(string), strval)\n\t}\n}\n\nfunc TestWithRepanic(t *testing.T) {\n\topts := new(options)\n\tval := true\n\tf := WithRepanic(val)\n\tf(opts)\n\tif opts.repanic != val {\n\t\tt.Errorf(\"TestWithRepanic() = %v, want %v\", opts.repanic, val)\n\t}\n}\n\nfunc TestWithWaitForDelivery(t *testing.T) {\n\topts := new(options)\n\tval := true\n\tf := WithWaitForDelivery(val)\n\tf(opts)\n\tif opts.waitForDelivery != val {\n\t\tt.Errorf(\"TestWithWaitForDelivery() = %v, want %v\", opts.waitForDelivery, val)\n\t}\n}\n\nfunc TestWithTimeout(t *testing.T) {\n\topts := new(options)\n\tval := time.Second * 10\n\tf := WithTimeout(val)\n\tf(opts)\n\tif opts.timeout != val {\n\t\tt.Errorf(\"TestWithTimeout() = %v, want %v\", opts.timeout, val)\n\t}\n}\n"
  },
  {
    "path": "contrib/log/aliyun/aliyun.go",
    "content": "package aliyun\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"strconv\"\n\t\"time\"\n\n\tsls \"github.com/aliyun/aliyun-log-go-sdk\"\n\t\"github.com/aliyun/aliyun-log-go-sdk/producer\"\n\t\"google.golang.org/protobuf/proto\"\n\n\t\"github.com/go-kratos/kratos/v2/log\"\n)\n\n// Logger see more detail https://github.com/aliyun/aliyun-log-go-sdk\ntype Logger interface {\n\tlog.Logger\n\n\tGetProducer() *producer.Producer\n\tClose() error\n}\n\ntype aliyunLog struct {\n\tproducer *producer.Producer\n\topts     *options\n}\n\nfunc (a *aliyunLog) GetProducer() *producer.Producer {\n\treturn a.producer\n}\n\ntype options struct {\n\taccessKey    string\n\taccessSecret string\n\tendpoint     string\n\tproject      string\n\tlogstore     string\n}\n\nfunc defaultOptions() *options {\n\treturn &options{\n\t\tproject:  \"projectName\",\n\t\tlogstore: \"app\",\n\t}\n}\n\nfunc WithEndpoint(endpoint string) Option {\n\treturn func(alc *options) {\n\t\talc.endpoint = endpoint\n\t}\n}\n\nfunc WithProject(project string) Option {\n\treturn func(alc *options) {\n\t\talc.project = project\n\t}\n}\n\nfunc WithLogstore(logstore string) Option {\n\treturn func(alc *options) {\n\t\talc.logstore = logstore\n\t}\n}\n\nfunc WithAccessKey(ak string) Option {\n\treturn func(alc *options) {\n\t\talc.accessKey = ak\n\t}\n}\n\nfunc WithAccessSecret(as string) Option {\n\treturn func(alc *options) {\n\t\talc.accessSecret = as\n\t}\n}\n\ntype Option func(alc *options)\n\nfunc (a *aliyunLog) Close() error {\n\treturn a.producer.Close(5000)\n}\n\nfunc (a *aliyunLog) Log(level log.Level, keyvals ...any) error {\n\tcontents := make([]*sls.LogContent, 0, len(keyvals)/2+1)\n\n\tcontents = append(contents, &sls.LogContent{\n\t\tKey:   newString(level.Key()),\n\t\tValue: newString(level.String()),\n\t})\n\tfor i := 0; i < len(keyvals); i += 2 {\n\t\tcontents = append(contents, &sls.LogContent{\n\t\t\tKey:   newString(toString(keyvals[i])),\n\t\t\tValue: newString(toString(keyvals[i+1])),\n\t\t})\n\t}\n\n\tlogInst := &sls.Log{\n\t\tTime:     proto.Uint32(uint32(time.Now().Unix())),\n\t\tContents: contents,\n\t}\n\treturn a.producer.SendLog(a.opts.project, a.opts.logstore, \"\", \"\", logInst)\n}\n\n// NewAliyunLog new aliyun logger with options.\nfunc NewAliyunLog(options ...Option) (Logger, error) {\n\topts := defaultOptions()\n\tfor _, o := range options {\n\t\to(opts)\n\t}\n\n\tproducerConfig := producer.GetDefaultProducerConfig()\n\tproducerConfig.Endpoint = opts.endpoint\n\tproducerConfig.AccessKeyID = opts.accessKey\n\tproducerConfig.AccessKeySecret = opts.accessSecret\n\tproducerInst, err := producer.NewProducer(producerConfig)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tproducerInst.Start()\n\n\treturn &aliyunLog{\n\t\topts:     opts,\n\t\tproducer: producerInst,\n\t}, nil\n}\n\n// newString string convert to *string\nfunc newString(s string) *string {\n\treturn &s\n}\n\n// toString convert any type to string\nfunc toString(v any) string {\n\tvar key string\n\tif v == nil {\n\t\treturn key\n\t}\n\tswitch v := v.(type) {\n\tcase float64:\n\t\tkey = strconv.FormatFloat(v, 'f', -1, 64)\n\tcase float32:\n\t\tkey = strconv.FormatFloat(float64(v), 'f', -1, 32)\n\tcase int:\n\t\tkey = strconv.Itoa(v)\n\tcase uint:\n\t\tkey = strconv.FormatUint(uint64(v), 10)\n\tcase int8:\n\t\tkey = strconv.Itoa(int(v))\n\tcase uint8:\n\t\tkey = strconv.FormatUint(uint64(v), 10)\n\tcase int16:\n\t\tkey = strconv.Itoa(int(v))\n\tcase uint16:\n\t\tkey = strconv.FormatUint(uint64(v), 10)\n\tcase int32:\n\t\tkey = strconv.Itoa(int(v))\n\tcase uint32:\n\t\tkey = strconv.FormatUint(uint64(v), 10)\n\tcase int64:\n\t\tkey = strconv.FormatInt(v, 10)\n\tcase uint64:\n\t\tkey = strconv.FormatUint(v, 10)\n\tcase string:\n\t\tkey = v\n\tcase bool:\n\t\tkey = strconv.FormatBool(v)\n\tcase []byte:\n\t\tkey = string(v)\n\tcase fmt.Stringer:\n\t\tkey = v.String()\n\tdefault:\n\t\tnewValue, _ := json.Marshal(v)\n\t\tkey = string(newValue)\n\t}\n\treturn key\n}\n"
  },
  {
    "path": "contrib/log/aliyun/aliyun_test.go",
    "content": "package aliyun\n\nimport (\n\t\"math\"\n\t\"reflect\"\n\t\"testing\"\n\n\t\"github.com/go-kratos/kratos/v2/log\"\n)\n\nfunc TestWithEndpoint(t *testing.T) {\n\topts := new(options)\n\tendpoint := \"foo\"\n\tfuncEndpoint := WithEndpoint(endpoint)\n\tfuncEndpoint(opts)\n\tif opts.endpoint != endpoint {\n\t\tt.Errorf(\"WithEndpoint() = %s, want %s\", opts.endpoint, endpoint)\n\t}\n}\n\nfunc TestWithProject(t *testing.T) {\n\topts := new(options)\n\tproject := \"foo\"\n\tfuncProject := WithProject(project)\n\tfuncProject(opts)\n\tif opts.project != project {\n\t\tt.Errorf(\"WithProject() = %s, want %s\", opts.project, project)\n\t}\n}\n\nfunc TestWithLogstore(t *testing.T) {\n\topts := new(options)\n\tlogstore := \"foo\"\n\tfuncLogstore := WithLogstore(logstore)\n\tfuncLogstore(opts)\n\tif opts.logstore != logstore {\n\t\tt.Errorf(\"WithLogatore() = %s, want %s\", opts.logstore, logstore)\n\t}\n}\n\nfunc TestWithAccessKey(t *testing.T) {\n\topts := new(options)\n\taccessKey := \"foo\"\n\tfuncAccessKey := WithAccessKey(accessKey)\n\tfuncAccessKey(opts)\n\tif opts.accessKey != accessKey {\n\t\tt.Errorf(\"WithAccessKey() = %s, want %s\", opts.accessKey, accessKey)\n\t}\n}\n\nfunc TestWithAccessSecret(t *testing.T) {\n\topts := new(options)\n\taccessSecret := \"foo\"\n\tfuncAccessSecret := WithAccessSecret(accessSecret)\n\tfuncAccessSecret(opts)\n\tif opts.accessSecret != accessSecret {\n\t\tt.Errorf(\"WithAccessSecret() = %s, want %s\", opts.accessSecret, accessSecret)\n\t}\n}\n\nfunc TestLogger(t *testing.T) {\n\tproject := \"foo\"\n\tlogger, err := NewAliyunLog(WithProject(project))\n\tif err != nil {\n\t\tt.Error(err)\n\t\treturn\n\t}\n\tdefer logger.Close()\n\tlogger.GetProducer()\n\tflog := log.NewHelper(logger)\n\tflog.Debug(\"log\", \"test\")\n\tflog.Info(\"log\", \"test\")\n\tflog.Warn(\"log\", \"test\")\n\tflog.Error(\"log\", \"test\")\n}\n\nfunc TestLog(t *testing.T) {\n\tproject := \"foo\"\n\tlogger, err := NewAliyunLog(WithProject(project))\n\tif err != nil {\n\t\tt.Error(err)\n\t\treturn\n\t}\n\tdefer logger.Close()\n\terr = logger.Log(log.LevelDebug, 0, int8(1), int16(2), int32(3))\n\tif err != nil {\n\t\tt.Errorf(\"Log() returns error:%v\", err)\n\t}\n\terr = logger.Log(log.LevelDebug, uint(0), uint8(1), uint16(2), uint32(3))\n\tif err != nil {\n\t\tt.Errorf(\"Log() returns error:%v\", err)\n\t}\n\terr = logger.Log(log.LevelDebug, uint(0), uint8(1), uint16(2), uint32(3))\n\tif err != nil {\n\t\tt.Errorf(\"Log() returns error:%v\", err)\n\t}\n\terr = logger.Log(log.LevelDebug, int64(0), uint64(1), float32(2), float64(3))\n\tif err != nil {\n\t\tt.Errorf(\"Log() returns error:%v\", err)\n\t}\n\terr = logger.Log(log.LevelDebug, []byte{0, 1, 2, 3}, \"foo\")\n\tif err != nil {\n\t\tt.Errorf(\"Log() returns error:%v\", err)\n\t}\n\terr = logger.Log(log.LevelDebug, true, 0)\n\tif err != nil {\n\t\tt.Errorf(\"Log() returns error:%v\", err)\n\t}\n}\n\nfunc TestNewString(t *testing.T) {\n\tptr := newString(\"\")\n\tif kind := reflect.TypeOf(ptr).Kind(); kind != reflect.Ptr {\n\t\tt.Errorf(\"want type: %v, got type: %v\", reflect.Ptr, kind)\n\t}\n}\n\nfunc TestToString(t *testing.T) {\n\ttests := []struct {\n\t\tname string\n\t\tin   any\n\t\tout  string\n\t}{\n\t\t{\"float64\", 6.66, \"6.66\"},\n\t\t{\"max float64\", math.MaxFloat64, \"179769313486231570000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\"}, //nolint:lll\n\t\t{\"float32\", float32(6.66), \"6.66\"},\n\t\t{\"max float32\", float32(math.MaxFloat32), \"340282350000000000000000000000000000000\"},\n\t\t{\"int\", math.MaxInt64, \"9223372036854775807\"},\n\t\t{\"uint\", uint(math.MaxUint64), \"18446744073709551615\"},\n\t\t{\"int8\", int8(math.MaxInt8), \"127\"},\n\t\t{\"uint8\", uint8(math.MaxUint8), \"255\"},\n\t\t{\"int16\", int16(math.MaxInt16), \"32767\"},\n\t\t{\"uint16\", uint16(math.MaxUint16), \"65535\"},\n\t\t{\"int32\", int32(math.MaxInt32), \"2147483647\"},\n\t\t{\"uint32\", uint32(math.MaxUint32), \"4294967295\"},\n\t\t{\"int64\", int64(math.MaxInt64), \"9223372036854775807\"},\n\t\t{\"uint64\", uint64(math.MaxUint64), \"18446744073709551615\"},\n\t\t{\"string\", \"abc\", \"abc\"},\n\t\t{\"bool\", false, \"false\"},\n\t\t{\"[]byte\", []byte(\"abc\"), \"abc\"},\n\t\t{\"struct\", struct{ Name string }{}, `{\"Name\":\"\"}`},\n\t\t{\"nil\", nil, \"\"},\n\t}\n\tfor _, test := range tests {\n\t\tt.Run(test.name, func(t *testing.T) {\n\t\t\tout := toString(test.in)\n\t\t\tif test.out != out {\n\t\t\t\tt.Fatalf(\"want: %s, got: %s\", test.out, out)\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "contrib/log/aliyun/go.mod",
    "content": "module github.com/go-kratos/kratos/contrib/log/aliyun/v2\n\ngo 1.22\n\nrequire (\n\tgithub.com/aliyun/aliyun-log-go-sdk v0.1.99\n\tgithub.com/go-kratos/kratos/v2 v2.9.2\n\tgoogle.golang.org/protobuf v1.33.0\n)\n\nrequire (\n\tgithub.com/cenkalti/backoff v2.2.1+incompatible // indirect\n\tgithub.com/go-kit/kit v0.10.0 // indirect\n\tgithub.com/go-logfmt/logfmt v0.5.1 // indirect\n\tgithub.com/gogo/protobuf v1.3.2 // indirect\n\tgithub.com/golang/protobuf v1.5.4 // indirect\n\tgithub.com/klauspost/compress v1.17.8 // indirect\n\tgithub.com/pierrec/lz4/v4 v4.1.22 // indirect\n\tgithub.com/pkg/errors v0.9.1 // indirect\n\tgo.uber.org/atomic v1.9.0 // indirect\n\tgolang.org/x/net v0.33.0 // indirect\n\tgopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect\n)\n\nreplace github.com/go-kratos/kratos/v2 => ../../../\n"
  },
  {
    "path": "contrib/log/aliyun/go.sum",
    "content": "cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=\ncloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=\ngithub.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=\ngithub.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=\ngithub.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0=\ngithub.com/Netflix/go-env v0.0.0-20220526054621-78278af1949d h1:wvStE9wLpws31NiWUx+38wny1msZ/tm+eL5xmm4Y7So=\ngithub.com/Netflix/go-env v0.0.0-20220526054621-78278af1949d/go.mod h1:9XMFaCeRyW7fC9XJOWQ+NdAv8VLG7ys7l3x4ozEGLUQ=\ngithub.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=\ngithub.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=\ngithub.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g=\ngithub.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c=\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/alibabacloud-go/alibabacloud-gateway-spi v0.0.4 h1:iC9YFYKDGEy3n/FtqJnOkZsene9olVspKmkX5A2YBEo=\ngithub.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.4/go.mod h1:sCavSAvdzOjul4cEqeVtvlSaSScfNsTQ+46HwlTL1hc=\ngithub.com/alibabacloud-go/darabonba-openapi/v2 v2.0.4 h1:7Q2FEyqxeZeIkwYMwRC3uphxV4i7O2eV4ETe21d6lS4=\ngithub.com/alibabacloud-go/darabonba-openapi/v2 v2.0.4/go.mod h1:5JHVmnHvGzR2wNdgaW1zDLQG8kOC4Uec8ubkMogW7OQ=\ngithub.com/alibabacloud-go/debug v0.0.0-20190504072949-9472017b5c68 h1:NqugFkGxx1TXSh/pBcU00Y6bljgDPaFdh5MUSeJ7e50=\ngithub.com/alibabacloud-go/debug v0.0.0-20190504072949-9472017b5c68/go.mod h1:6pb/Qy8c+lqua8cFpEy7g39NRRqOWc3rOwAy8m5Y2BY=\ngithub.com/alibabacloud-go/endpoint-util v1.1.0 h1:r/4D3VSw888XGaeNpP994zDUaxdgTSHBbVfZlzf6b5Q=\ngithub.com/alibabacloud-go/endpoint-util v1.1.0/go.mod h1:O5FuCALmCKs2Ff7JFJMudHs0I5EBgecXXxZRyswlEjE=\ngithub.com/alibabacloud-go/openapi-util v0.1.0 h1:0z75cIULkDrdEhkLWgi9tnLe+KhAFE/r5Pb3312/eAY=\ngithub.com/alibabacloud-go/openapi-util v0.1.0/go.mod h1:sQuElr4ywwFRlCCberQwKRFhRzIyG4QTP/P4y1CJ6Ws=\ngithub.com/alibabacloud-go/sts-20150401/v2 v2.0.1 h1:CevZp0VdG7Q+1J3qwNj+JL7ztKxsL27+tknbdTK9Y6M=\ngithub.com/alibabacloud-go/sts-20150401/v2 v2.0.1/go.mod h1:8wJW1xC4mVcdRXzOvWJYfCCxmvFzZ0VB9iilVjBeWBc=\ngithub.com/alibabacloud-go/tea v1.1.19 h1:Xroq0M+pr0mC834Djj3Fl4ZA8+GGoA0i7aWse1vmgf4=\ngithub.com/alibabacloud-go/tea v1.1.19/go.mod h1:nXxjm6CIFkBhwW4FQkNrolwbfon8Svy6cujmKFUq98A=\ngithub.com/alibabacloud-go/tea-utils v1.3.1 h1:iWQeRzRheqCMuiF3+XkfybB3kTgUXkXX+JMrqfLeB2I=\ngithub.com/alibabacloud-go/tea-utils v1.3.1/go.mod h1:EI/o33aBfj3hETm4RLiAxF/ThQdSngxrpF8rKUDJjPE=\ngithub.com/alibabacloud-go/tea-utils/v2 v2.0.1 h1:K6kwgo+UiYx+/kr6CO0PN5ACZDzE3nnn9d77215AkTs=\ngithub.com/alibabacloud-go/tea-utils/v2 v2.0.1/go.mod h1:U5MTY10WwlquGPS34DOeomUGBB0gXbLueiq5Trwu0C4=\ngithub.com/alibabacloud-go/tea-xml v1.1.2 h1:oLxa7JUXm2EDFzMg+7oRsYc+kutgCVwm+bZlhhmvW5M=\ngithub.com/alibabacloud-go/tea-xml v1.1.2/go.mod h1:Rq08vgCcCAjHyRi/M7xlHKUykZCEtyBy9+DPF6GgEu8=\ngithub.com/aliyun/aliyun-log-go-sdk v0.1.99 h1:gZvHwShUzR6F9ttBLhC4+eEC2oT4NVp5bEroPUxMeDg=\ngithub.com/aliyun/aliyun-log-go-sdk v0.1.99/go.mod h1:1NZbf//4a26kGXem8pb3/vc71M+XqRYQgekEZv89y4U=\ngithub.com/aliyun/credentials-go v1.1.2 h1:qU1vwGIBb3UJ8BwunHDRFtAhS6jnQLnde/yk0+Ih2GY=\ngithub.com/aliyun/credentials-go v1.1.2/go.mod h1:ozcZaMR5kLM7pwtCMEpVmQ242suV6qTJya2bDq4X1Tw=\ngithub.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=\ngithub.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=\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-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=\ngithub.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A=\ngithub.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU=\ngithub.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=\ngithub.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g=\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/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ=\ngithub.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4=\ngithub.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=\ngithub.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=\ngithub.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=\ngithub.com/clbanning/mxj/v2 v2.5.5 h1:oT81vUeEiQQ/DcHbzSytRngP6Ky9O+L+0Bw0zSJag9E=\ngithub.com/clbanning/mxj/v2 v2.5.5/go.mod h1:hNiWqW14h+kc+MdF9C6/YoRfjEJoR3ou6tn/Qo+ve2s=\ngithub.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE=\ngithub.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=\ngithub.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8=\ngithub.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI=\ngithub.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=\ngithub.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=\ngithub.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=\ngithub.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=\ngithub.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=\ngithub.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.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/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=\ngithub.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=\ngithub.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=\ngithub.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU=\ngithub.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=\ngithub.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=\ngithub.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g=\ngithub.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=\ngithub.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=\ngithub.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=\ngithub.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4=\ngithub.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20=\ngithub.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=\ngithub.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=\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-kit/kit v0.10.0 h1:dXFJfIHVvUcpSgDOV+Ne6t7jXri8Tfv2uOLHUZ2XNuo=\ngithub.com/go-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgOZ7o=\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-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=\ngithub.com/go-logfmt/logfmt v0.5.1 h1:otpy5pqBCBZ1ng9RQ0dPu4PN7ba75Y/aA+UpowDyNVA=\ngithub.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=\ngithub.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=\ngithub.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk=\ngithub.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=\ngithub.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s=\ngithub.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=\ngithub.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=\ngithub.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=\ngithub.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=\ngithub.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=\ngithub.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=\ngithub.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=\ngithub.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=\ngithub.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=\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/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=\ngithub.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=\ngithub.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=\ngithub.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=\ngithub.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=\ngithub.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=\ngithub.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=\ngithub.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=\ngithub.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=\ngithub.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=\ngithub.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=\ngithub.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=\ngithub.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=\ngithub.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=\ngithub.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=\ngithub.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=\ngithub.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=\ngithub.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=\ngithub.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE=\ngithub.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=\ngithub.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=\ngithub.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=\ngithub.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=\ngithub.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=\ngithub.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=\ngithub.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=\ngithub.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=\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-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=\ngithub.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=\ngithub.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=\ngithub.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=\ngithub.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=\ngithub.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=\ngithub.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=\ngithub.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=\ngithub.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=\ngithub.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg=\ngithub.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=\ngithub.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo=\ngithub.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=\ngithub.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=\ngithub.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=\ngithub.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=\ngithub.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=\ngithub.com/json-iterator/go v1.1.10 h1:Kz6Cvnvv2wGdaG/V8yMvfkmNiXq9Ya2KUv4rouJJr68=\ngithub.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=\ngithub.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=\ngithub.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=\ngithub.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=\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.17.8 h1:YcnTYrq7MikUT7k0Yb5eceMmALQPYBW/Xltxn0NAMnU=\ngithub.com/klauspost/compress v1.17.8/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=\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/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/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM=\ngithub.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4=\ngithub.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ=\ngithub.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=\ngithub.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=\ngithub.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=\ngithub.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=\ngithub.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=\ngithub.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=\ngithub.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=\ngithub.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=\ngithub.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=\ngithub.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=\ngithub.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=\ngithub.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=\ngithub.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=\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 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=\ngithub.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=\ngithub.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=\ngithub.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg=\ngithub.com/nats-io/jwt v0.3.2/go.mod h1:/euKqTS1ZD+zzjYrY7pseZrTtWQSjujC7xjPc8wL6eU=\ngithub.com/nats-io/nats-server/v2 v2.1.2/go.mod h1:Afk+wRZqkMQs/p45uXdrVLuab3gwv3Z8C4HTBu8GD/k=\ngithub.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzEE/Zbp4w=\ngithub.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w=\ngithub.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w=\ngithub.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=\ngithub.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs=\ngithub.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=\ngithub.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=\ngithub.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=\ngithub.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=\ngithub.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=\ngithub.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk=\ngithub.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis=\ngithub.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74=\ngithub.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=\ngithub.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=\ngithub.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5/go.mod h1:/wsWhb9smxSfWAKL3wpBW7V8scJMt8N8gnaMCS9E/cA=\ngithub.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw=\ngithub.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4=\ngithub.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4=\ngithub.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM=\ngithub.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=\ngithub.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=\ngithub.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac=\ngithub.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc=\ngithub.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=\ngithub.com/pierrec/lz4/v4 v4.1.22 h1:cKFw6uJDK+/gfw5BcDL0JL5aBsAFdsIT18eRtLj7VIU=\ngithub.com/pierrec/lz4/v4 v4.1.22/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=\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/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA=\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/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=\ngithub.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=\ngithub.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs=\ngithub.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=\ngithub.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og=\ngithub.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=\ngithub.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=\ngithub.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=\ngithub.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=\ngithub.com/prometheus/client_model v0.1.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=\ngithub.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=\ngithub.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=\ngithub.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA=\ngithub.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=\ngithub.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/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/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=\ngithub.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=\ngithub.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=\ngithub.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=\ngithub.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=\ngithub.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E=\ngithub.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=\ngithub.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=\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/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=\ngithub.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=\ngithub.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=\ngithub.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY=\ngithub.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=\ngithub.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=\ngithub.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw=\ngithub.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw=\ngithub.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI=\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/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.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=\ngithub.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=\ngithub.com/tjfoc/gmsm v1.3.2 h1:7JVkAn5bvUJ7HtU08iW6UiD+UTmJTIToHCfeFzkcCxM=\ngithub.com/tjfoc/gmsm v1.3.2/go.mod h1:HaUcFuY0auTiaHB9MHFGCPx5IaLhTUd2atbCFBQXn9w=\ngithub.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=\ngithub.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=\ngithub.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=\ngithub.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=\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/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=\ngo.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg=\ngo.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=\ngo.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=\ngo.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=\ngo.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=\ngo.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=\ngo.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE=\ngo.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=\ngo.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=\ngo.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4=\ngo.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=\ngo.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=\ngo.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=\ngolang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=\ngolang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/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-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\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/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=\ngolang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=\ngolang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=\ngolang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=\ngolang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=\ngolang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=\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-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=\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-20190813141303-74dc4d7220e7/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.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I=\ngolang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=\ngolang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=\ngolang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=\ngolang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\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-20190227155943-e225da77a7e6/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.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=\ngolang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=\ngolang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20180830151530-49385e6e1522/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-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/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-20181122145206-62eef0e2fa9b/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-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-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\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/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=\ngolang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=\ngolang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=\ngolang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=\ngolang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=\ngolang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=\ngolang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=\ngolang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=\ngolang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/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-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\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 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=\ngolang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngoogle.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk=\ngoogle.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=\ngoogle.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=\ngoogle.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=\ngoogle.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=\ngoogle.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=\ngoogle.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=\ngoogle.golang.org/genproto v0.0.0-20190530194941-fb225487d101/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s=\ngoogle.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=\ngoogle.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=\ngoogle.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=\ngoogle.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM=\ngoogle.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=\ngoogle.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=\ngoogle.golang.org/grpc v1.22.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=\ngoogle.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=\ngoogle.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=\ngoogle.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=\ngoogle.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=\ngoogle.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=\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-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=\ngopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=\ngopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=\ngopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o=\ngopkg.in/ini.v1 v1.56.0 h1:DPMeDvGTM54DXbPkVIZsp19fp/I2K7zwA/itHYHKo8Y=\ngopkg.in/ini.v1 v1.56.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=\ngopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8=\ngopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=\ngopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=\ngopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=\ngopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=\ngopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=\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.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=\ngopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\nhonnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\nhonnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\nhonnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\nhonnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=\nsigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=\nsourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU=\n"
  },
  {
    "path": "contrib/log/fluent/fluent.go",
    "content": "package fluent\n\nimport (\n\t\"fmt\"\n\t\"net\"\n\t\"net/url\"\n\t\"strconv\"\n\t\"time\"\n\n\t\"github.com/fluent/fluent-logger-golang/fluent\"\n\n\t\"github.com/go-kratos/kratos/v2/log\"\n)\n\nvar _ log.Logger = (*Logger)(nil)\n\n// Option is fluentd logger option.\ntype Option func(*options)\n\ntype options struct {\n\ttimeout            time.Duration\n\twriteTimeout       time.Duration\n\tbufferLimit        int\n\tretryWait          int\n\tmaxRetry           int\n\tmaxRetryWait       int\n\ttagPrefix          string\n\tasync              bool\n\tforceStopAsyncSend bool\n}\n\n// WithTimeout with config Timeout.\nfunc WithTimeout(timeout time.Duration) Option {\n\treturn func(opts *options) {\n\t\topts.timeout = timeout\n\t}\n}\n\n// WithWriteTimeout with config WriteTimeout.\nfunc WithWriteTimeout(writeTimeout time.Duration) Option {\n\treturn func(opts *options) {\n\t\topts.writeTimeout = writeTimeout\n\t}\n}\n\n// WithBufferLimit with config BufferLimit.\nfunc WithBufferLimit(bufferLimit int) Option {\n\treturn func(opts *options) {\n\t\topts.bufferLimit = bufferLimit\n\t}\n}\n\n// WithRetryWait with config RetryWait.\nfunc WithRetryWait(retryWait int) Option {\n\treturn func(opts *options) {\n\t\topts.retryWait = retryWait\n\t}\n}\n\n// WithMaxRetry with config MaxRetry.\nfunc WithMaxRetry(maxRetry int) Option {\n\treturn func(opts *options) {\n\t\topts.maxRetry = maxRetry\n\t}\n}\n\n// WithMaxRetryWait with config MaxRetryWait.\nfunc WithMaxRetryWait(maxRetryWait int) Option {\n\treturn func(opts *options) {\n\t\topts.maxRetryWait = maxRetryWait\n\t}\n}\n\n// WithTagPrefix with config TagPrefix.\nfunc WithTagPrefix(tagPrefix string) Option {\n\treturn func(opts *options) {\n\t\topts.tagPrefix = tagPrefix\n\t}\n}\n\n// WithAsync with config Async.\nfunc WithAsync(async bool) Option {\n\treturn func(opts *options) {\n\t\topts.async = async\n\t}\n}\n\n// WithForceStopAsyncSend with config ForceStopAsyncSend.\nfunc WithForceStopAsyncSend(forceStopAsyncSend bool) Option {\n\treturn func(opts *options) {\n\t\topts.forceStopAsyncSend = forceStopAsyncSend\n\t}\n}\n\n// Logger is fluent logger sdk.\ntype Logger struct {\n\topts options\n\tlog  *fluent.Fluent\n}\n\n// NewLogger new a std logger with options.\n// target:\n//\n//\ttcp://127.0.0.1:24224\n//\tunix://var/run/fluent/fluent.sock\nfunc NewLogger(addr string, opts ...Option) (*Logger, error) {\n\toption := options{}\n\tfor _, o := range opts {\n\t\to(&option)\n\t}\n\tu, err := url.Parse(addr)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tc := fluent.Config{\n\t\tTimeout:            option.timeout,\n\t\tWriteTimeout:       option.writeTimeout,\n\t\tBufferLimit:        option.bufferLimit,\n\t\tRetryWait:          option.retryWait,\n\t\tMaxRetry:           option.maxRetry,\n\t\tMaxRetryWait:       option.maxRetryWait,\n\t\tTagPrefix:          option.tagPrefix,\n\t\tAsync:              option.async,\n\t\tForceStopAsyncSend: option.forceStopAsyncSend,\n\t}\n\tswitch u.Scheme {\n\tcase \"tcp\":\n\t\thost, port, err2 := net.SplitHostPort(u.Host)\n\t\tif err2 != nil {\n\t\t\treturn nil, err2\n\t\t}\n\t\tif c.FluentPort, err = strconv.Atoi(port); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tc.FluentNetwork = u.Scheme\n\t\tc.FluentHost = host\n\tcase \"unix\":\n\t\tc.FluentNetwork = u.Scheme\n\t\tc.FluentSocketPath = u.Path\n\tdefault:\n\t\treturn nil, fmt.Errorf(\"unknown network: %s\", u.Scheme)\n\t}\n\tfl, err := fluent.New(c)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn &Logger{\n\t\topts: option,\n\t\tlog:  fl,\n\t}, nil\n}\n\n// Log print the kv pairs log.\nfunc (l *Logger) Log(level log.Level, keyvals ...any) error {\n\tif len(keyvals) == 0 {\n\t\treturn nil\n\t}\n\tif len(keyvals)%2 != 0 {\n\t\tkeyvals = append(keyvals, \"KEYVALS UNPAIRED\")\n\t}\n\n\tdata := make(map[string]string, len(keyvals)/2+1)\n\n\tfor i := 0; i < len(keyvals); i += 2 {\n\t\tdata[fmt.Sprint(keyvals[i])] = fmt.Sprint(keyvals[i+1])\n\t}\n\n\tif err := l.log.Post(level.String(), data); err != nil {\n\t\tprintln(err)\n\t}\n\treturn nil\n}\n\n// Close close the logger.\nfunc (l *Logger) Close() error {\n\treturn l.log.Close()\n}\n"
  },
  {
    "path": "contrib/log/fluent/fluent_test.go",
    "content": "package fluent\n\nimport (\n\t\"io\"\n\t\"net\"\n\t\"os\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/go-kratos/kratos/v2/log\"\n)\n\nfunc TestMain(m *testing.M) {\n\tlistener := func(ln net.Listener) {\n\t\tconn, err := ln.Accept()\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t\tdefer conn.Close()\n\t\t_, err = io.ReadAll(conn)\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t}\n\n\tif ln, err := net.Listen(\"tcp\", \":24224\"); err == nil {\n\t\tdefer ln.Close()\n\t\tgo func() {\n\t\t\tfor {\n\t\t\t\tlistener(ln)\n\t\t\t}\n\t\t}()\n\t}\n\n\tos.Exit(m.Run())\n}\n\nfunc TestWithTimeout(t *testing.T) {\n\topts := new(options)\n\tvar duration time.Duration = 1000000000\n\tfuncTimeout := WithTimeout(duration)\n\tfuncTimeout(opts)\n\tif opts.timeout != duration {\n\t\tt.Errorf(\"WithTimeout() = %v, want %v\", opts.timeout, duration)\n\t}\n}\n\nfunc TestWithWriteTimeout(t *testing.T) {\n\topts := new(options)\n\tvar duration time.Duration = 1000000000\n\tfuncWriteTimeout := WithWriteTimeout(duration)\n\tfuncWriteTimeout(opts)\n\tif opts.writeTimeout != duration {\n\t\tt.Errorf(\"WithWriteTimeout() = %v, want %v\", opts.writeTimeout, duration)\n\t}\n}\n\nfunc TestWithBufferLimit(t *testing.T) {\n\topts := new(options)\n\tbufferLimit := 1000000000\n\tfuncBufferLimit := WithBufferLimit(bufferLimit)\n\tfuncBufferLimit(opts)\n\tif opts.bufferLimit != bufferLimit {\n\t\tt.Errorf(\"WithBufferLimit() = %d, want %d\", opts.bufferLimit, bufferLimit)\n\t}\n}\n\nfunc TestWithRetryWait(t *testing.T) {\n\topts := new(options)\n\tretryWait := 1000000000\n\tfuncRetryWait := WithRetryWait(retryWait)\n\tfuncRetryWait(opts)\n\tif opts.retryWait != retryWait {\n\t\tt.Errorf(\"WithRetryWait() = %d, want %d\", opts.retryWait, retryWait)\n\t}\n}\n\nfunc TestWithMaxRetry(t *testing.T) {\n\topts := new(options)\n\tmaxRetry := 1000000000\n\tfuncMaxRetry := WithMaxRetry(maxRetry)\n\tfuncMaxRetry(opts)\n\tif opts.maxRetry != maxRetry {\n\t\tt.Errorf(\"WithMaxRetry() = %d, want %d\", opts.maxRetry, maxRetry)\n\t}\n}\n\nfunc TestWithMaxRetryWait(t *testing.T) {\n\topts := new(options)\n\tmaxRetryWait := 1000000000\n\tfuncMaxRetryWait := WithMaxRetryWait(maxRetryWait)\n\tfuncMaxRetryWait(opts)\n\tif opts.maxRetryWait != maxRetryWait {\n\t\tt.Errorf(\"WithMaxRetryWait() = %d, want %d\", opts.maxRetryWait, maxRetryWait)\n\t}\n}\n\nfunc TestWithTagPrefix(t *testing.T) {\n\topts := new(options)\n\ttagPrefix := \"tag_prefix\"\n\tfuncTagPrefix := WithTagPrefix(tagPrefix)\n\tfuncTagPrefix(opts)\n\tif opts.tagPrefix != tagPrefix {\n\t\tt.Errorf(\"WithTagPrefix() = %s, want %s\", opts.tagPrefix, tagPrefix)\n\t}\n}\n\nfunc TestWithAsync(t *testing.T) {\n\topts := new(options)\n\tasync := true\n\tfuncAsync := WithAsync(async)\n\tfuncAsync(opts)\n\tif opts.async != async {\n\t\tt.Errorf(\"WithAsync() = %t, want %t\", opts.async, async)\n\t}\n}\n\nfunc TestWithForceStopAsyncSend(t *testing.T) {\n\topts := new(options)\n\tforceStopAsyncSend := true\n\tfuncForceStopAsyncSend := WithForceStopAsyncSend(forceStopAsyncSend)\n\tfuncForceStopAsyncSend(opts)\n\tif opts.forceStopAsyncSend != forceStopAsyncSend {\n\t\tt.Errorf(\"WithForceStopAsyncSend() = %t, want %t\", opts.forceStopAsyncSend, forceStopAsyncSend)\n\t}\n}\n\nfunc TestLogger(t *testing.T) {\n\tlogger, err := NewLogger(\"tcp://127.0.0.1:24224\")\n\tif err != nil {\n\t\tt.Error(err)\n\t}\n\tdefer logger.Close()\n\tflog := log.NewHelper(logger)\n\n\tflog.Debug(\"log\", \"test\")\n\tflog.Info(\"log\", \"test\")\n\tflog.Warn(\"log\", \"test\")\n\tflog.Error(\"log\", \"test\")\n}\n\nfunc TestLoggerWithOpt(t *testing.T) {\n\tvar duration time.Duration = 1000000000\n\tlogger, err := NewLogger(\"tcp://127.0.0.1:24224\", WithTimeout(duration))\n\tif err != nil {\n\t\tt.Error(err)\n\t}\n\tdefer logger.Close()\n\tflog := log.NewHelper(logger)\n\n\tflog.Debug(\"log\", \"test\")\n\tflog.Info(\"log\", \"test\")\n\tflog.Warn(\"log\", \"test\")\n\tflog.Error(\"log\", \"test\")\n}\n\nfunc TestLoggerError(t *testing.T) {\n\terrCase := []string{\n\t\t\"foo\",\n\t\t\"tcp://127.0.0.1/\",\n\t\t\"tcp://127.0.0.1:1234a\",\n\t\t\"tcp://127.0.0.1:65535\",\n\t\t\"https://127.0.0.1:8080\",\n\t\t\"unix://foo/bar\",\n\t}\n\tfor _, errc := range errCase {\n\t\t_, err := NewLogger(errc)\n\t\tif err == nil {\n\t\t\tt.Error(err)\n\t\t}\n\t}\n}\n\nfunc BenchmarkLoggerPrint(b *testing.B) {\n\tb.SetParallelism(100)\n\tlogger, err := NewLogger(\"tcp://127.0.0.1:24224\")\n\tflog := log.NewHelper(logger)\n\tif err != nil {\n\t\tb.Error(err)\n\t}\n\tdefer logger.Close()\n\tb.RunParallel(func(pb *testing.PB) {\n\t\tfor pb.Next() {\n\t\t\tflog.Info(\"log\", \"test\")\n\t\t}\n\t})\n}\n\nfunc BenchmarkLoggerHelperV(b *testing.B) {\n\tb.SetParallelism(100)\n\tlogger, err := NewLogger(\"tcp://127.0.0.1:24224\")\n\tif err != nil {\n\t\tb.Error(err)\n\t}\n\th := log.NewHelper(logger)\n\tb.RunParallel(func(pb *testing.PB) {\n\t\tfor pb.Next() {\n\t\t\th.Info(\"log\", \"test\")\n\t\t}\n\t})\n}\n\nfunc BenchmarkLoggerHelperInfo(b *testing.B) {\n\tb.SetParallelism(100)\n\tlogger, err := NewLogger(\"tcp://127.0.0.1:24224\")\n\tif err != nil {\n\t\tb.Error(err)\n\t}\n\tdefer logger.Close()\n\th := log.NewHelper(logger)\n\tb.RunParallel(func(pb *testing.PB) {\n\t\tfor pb.Next() {\n\t\t\th.Info(\"test\")\n\t\t}\n\t})\n}\n\nfunc BenchmarkLoggerHelperInfof(b *testing.B) {\n\tb.SetParallelism(100)\n\tlogger, err := NewLogger(\"tcp://127.0.0.1:24224\")\n\tif err != nil {\n\t\tb.Error(err)\n\t}\n\tdefer logger.Close()\n\th := log.NewHelper(logger)\n\tb.RunParallel(func(pb *testing.PB) {\n\t\tfor pb.Next() {\n\t\t\th.Infof(\"log %s\", \"test\")\n\t\t}\n\t})\n}\n\nfunc BenchmarkLoggerHelperInfow(b *testing.B) {\n\tb.SetParallelism(100)\n\tlogger, err := NewLogger(\"tcp://127.0.0.1:24224\")\n\tif err != nil {\n\t\tb.Error(err)\n\t}\n\tdefer logger.Close()\n\th := log.NewHelper(logger)\n\tb.RunParallel(func(pb *testing.PB) {\n\t\tfor pb.Next() {\n\t\t\th.Infow(\"log\", \"test\")\n\t\t}\n\t})\n}\n"
  },
  {
    "path": "contrib/log/fluent/go.mod",
    "content": "module github.com/go-kratos/kratos/contrib/log/fluent/v2\n\ngo 1.22\n\nrequire (\n\tgithub.com/fluent/fluent-logger-golang v1.9.0\n\tgithub.com/go-kratos/kratos/v2 v2.9.2\n)\n\nrequire (\n\tgithub.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 // indirect\n\tgithub.com/kr/text v0.2.0 // indirect\n\tgithub.com/philhofer/fwd v1.1.1 // indirect\n\tgithub.com/tinylib/msgp v1.1.6 // indirect\n)\n\nreplace github.com/go-kratos/kratos/v2 => ../../../\n"
  },
  {
    "path": "contrib/log/fluent/go.sum",
    "content": "github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 h1:DDGfHa7BWjL4YnC6+E63dPcxHo2sUxDIu8g3QgEJdRY=\ngithub.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4=\ngithub.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=\ngithub.com/fluent/fluent-logger-golang v1.9.0 h1:zUdY44CHX2oIUc7VTNZc+4m+ORuO/mldQDA7czhWXEg=\ngithub.com/fluent/fluent-logger-golang v1.9.0/go.mod h1:2/HCT/jTy78yGyeNGQLGQsjF3zzzAuy6Xlk6FCMV5eU=\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/philhofer/fwd v1.1.1 h1:GdGcTjf5RNAxwS4QLsiMzJYj5KEvPJD3Abr261yRQXQ=\ngithub.com/philhofer/fwd v1.1.1/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU=\ngithub.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=\ngithub.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=\ngithub.com/tinylib/msgp v1.1.6 h1:i+SbKraHhnrf9M5MYmvQhFnbLhAXSDWF8WWsuyRdocw=\ngithub.com/tinylib/msgp v1.1.6/go.mod h1:75BAfg2hauQhs3qedfdDZmWAPcFMAvJE5b9rGOMufyw=\ngithub.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=\ngolang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=\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/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=\ngolang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=\ngolang.org/x/sync v0.0.0-20190423024810-112230192c58/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.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=\ngolang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=\ngolang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/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-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\ngolang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20201022035929-9cf592e881e9/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-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\n"
  },
  {
    "path": "contrib/log/logrus/go.mod",
    "content": "module github.com/go-kratos/kratos/contrib/log/logrus/v2\n\ngo 1.22\n\nrequire (\n\tgithub.com/go-kratos/kratos/v2 v2.9.2\n\tgithub.com/sirupsen/logrus v1.8.1\n)\n\nrequire golang.org/x/sys v0.28.0 // indirect\n\nreplace github.com/go-kratos/kratos/v2 => ../../../\n"
  },
  {
    "path": "contrib/log/logrus/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/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=\ngithub.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=\ngithub.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=\ngithub.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=\ngithub.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=\ngithub.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=\ngolang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=\ngolang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=\ngolang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=\ngolang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=\n"
  },
  {
    "path": "contrib/log/logrus/logrus.go",
    "content": "package logrus\n\nimport (\n\t\"github.com/sirupsen/logrus\"\n\n\t\"github.com/go-kratos/kratos/v2/log\"\n)\n\nvar _ log.Logger = (*Logger)(nil)\n\ntype Logger struct {\n\tlog *logrus.Logger\n}\n\nfunc NewLogger(logger *logrus.Logger) log.Logger {\n\treturn &Logger{\n\t\tlog: logger,\n\t}\n}\n\nfunc (l *Logger) Log(level log.Level, keyvals ...any) (err error) {\n\tvar (\n\t\tlogrusLevel logrus.Level\n\t\tfields      logrus.Fields = make(map[string]any)\n\t\tmsg         string\n\t)\n\n\tswitch level {\n\tcase log.LevelDebug:\n\t\tlogrusLevel = logrus.DebugLevel\n\tcase log.LevelInfo:\n\t\tlogrusLevel = logrus.InfoLevel\n\tcase log.LevelWarn:\n\t\tlogrusLevel = logrus.WarnLevel\n\tcase log.LevelError:\n\t\tlogrusLevel = logrus.ErrorLevel\n\tcase log.LevelFatal:\n\t\tlogrusLevel = logrus.FatalLevel\n\tdefault:\n\t\tlogrusLevel = logrus.DebugLevel\n\t}\n\n\tif logrusLevel > l.log.Level {\n\t\treturn\n\t}\n\n\tif len(keyvals) == 0 {\n\t\treturn nil\n\t}\n\tif len(keyvals)%2 != 0 {\n\t\tkeyvals = append(keyvals, \"\")\n\t}\n\tfor i := 0; i < len(keyvals); i += 2 {\n\t\tkey, ok := keyvals[i].(string)\n\t\tif !ok {\n\t\t\tcontinue\n\t\t}\n\t\tif key == logrus.FieldKeyMsg {\n\t\t\tmsg, _ = keyvals[i+1].(string)\n\t\t\tcontinue\n\t\t}\n\t\tfields[key] = keyvals[i+1]\n\t}\n\n\tif len(fields) > 0 {\n\t\tl.log.WithFields(fields).Log(logrusLevel, msg)\n\t} else {\n\t\tl.log.Log(logrusLevel, msg)\n\t}\n\n\treturn\n}\n"
  },
  {
    "path": "contrib/log/logrus/logrus_test.go",
    "content": "package logrus\n\nimport (\n\t\"bytes\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/sirupsen/logrus\"\n\n\t\"github.com/go-kratos/kratos/v2/log\"\n)\n\nfunc TestLoggerLog(t *testing.T) {\n\ttests := map[string]struct {\n\t\tlevel     logrus.Level\n\t\tformatter logrus.Formatter\n\t\tlogLevel  log.Level\n\t\tkvs       []any\n\t\twant      string\n\t}{\n\t\t\"json format\": {\n\t\t\tlevel:     logrus.InfoLevel,\n\t\t\tformatter: &logrus.JSONFormatter{},\n\t\t\tlogLevel:  log.LevelInfo,\n\t\t\tkvs:       []any{\"case\", \"json format\", \"msg\", \"1\"},\n\t\t\twant:      `{\"case\":\"json format\",\"level\":\"info\",\"msg\":\"1\"`,\n\t\t},\n\t\t\"level unmatch\": {\n\t\t\tlevel:     logrus.InfoLevel,\n\t\t\tformatter: &logrus.JSONFormatter{},\n\t\t\tlogLevel:  log.LevelDebug,\n\t\t\tkvs:       []any{\"case\", \"level unmatch\", \"msg\", \"1\"},\n\t\t\twant:      \"\",\n\t\t},\n\t\t\"fatal level\": {\n\t\t\tlevel:     logrus.InfoLevel,\n\t\t\tformatter: &logrus.JSONFormatter{},\n\t\t\tlogLevel:  log.LevelFatal,\n\t\t\tkvs:       []any{\"case\", \"json format\", \"msg\", \"1\"},\n\t\t\twant:      `{\"case\":\"json format\",\"level\":\"fatal\",\"msg\":\"1\"`,\n\t\t},\n\t\t\"no tags\": {\n\t\t\tlevel:     logrus.InfoLevel,\n\t\t\tformatter: &logrus.JSONFormatter{},\n\t\t\tlogLevel:  log.LevelInfo,\n\t\t\tkvs:       []any{\"msg\", \"1\"},\n\t\t\twant:      `{\"level\":\"info\",\"msg\":\"1\"`,\n\t\t},\n\t}\n\n\tfor name, test := range tests {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\toutput := new(bytes.Buffer)\n\t\t\tlogger := logrus.New()\n\t\t\tlogger.Level = test.level\n\t\t\tlogger.Out = output\n\t\t\tlogger.Formatter = test.formatter\n\t\t\twrapped := NewLogger(logger)\n\t\t\t_ = wrapped.Log(test.logLevel, test.kvs...)\n\n\t\t\tif !strings.HasPrefix(output.String(), test.want) {\n\t\t\t\tt.Errorf(\"TestName(%s): %s has not prefix %s\", name, output.String(), test.want)\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "contrib/log/tencent/go.mod",
    "content": "module github.com/go-kratos/kratos/contrib/log/tencent/v2\n\ngo 1.22\n\nrequire (\n\tgithub.com/go-kratos/kratos/v2 v2.9.2\n\tgithub.com/tencentcloud/tencentcloud-cls-sdk-go v1.0.2\n\tgoogle.golang.org/protobuf v1.33.0\n)\n\nrequire (\n\tgithub.com/golang/protobuf v1.5.4 // indirect\n\tgithub.com/klauspost/compress v1.15.1 // indirect\n\tgithub.com/pierrec/lz4 v2.6.1+incompatible // indirect\n\tgo.uber.org/atomic v1.9.0 // indirect\n)\n\nreplace github.com/go-kratos/kratos/v2 => ../../../\n"
  },
  {
    "path": "contrib/log/tencent/go.sum",
    "content": "github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=\ngithub.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.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.0 h1:+cqqvzZV87b4adx/5ayVOaYZ2CrvM4ejQvUdBzPPUss=\ngithub.com/frankban/quicktest v1.14.0/go.mod h1:NeW+ay9A/U67EYXNFA1nPE8e/tnQv/09mUdL/ijj8og=\ngithub.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=\ngithub.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=\ngithub.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=\ngithub.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=\ngithub.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/klauspost/compress v1.15.1 h1:y9FcTHGyrebwfP0ZZqFiaxTaiDnUrGkJkI+f583BL1A=\ngithub.com/klauspost/compress v1.15.1/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=\ngithub.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=\ngithub.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=\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/pierrec/lz4 v2.6.1+incompatible h1:9UY3+iC23yxF0UfGaYrGplQ+79Rg+h/q9FV9ix19jjM=\ngithub.com/pierrec/lz4 v2.6.1+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=\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.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=\ngithub.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=\ngithub.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=\ngithub.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\ngithub.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=\ngithub.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=\ngithub.com/tencentcloud/tencentcloud-cls-sdk-go v1.0.2 h1:29QoDxUqlFWd0Pgyt4lqGTGMNvJGxVQ3lRx1haRaPnw=\ngithub.com/tencentcloud/tencentcloud-cls-sdk-go v1.0.2/go.mod h1:WU+0TXfVbSctEsUUf4KmIKnfr+tknbjcsnx/TrEIPH4=\ngo.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE=\ngo.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=\ngolang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=\ngolang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=\ngolang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=\ngolang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngoogle.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=\ngoogle.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=\ngoogle.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=\ngoogle.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=\ngopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=\n"
  },
  {
    "path": "contrib/log/tencent/tencent.go",
    "content": "package tencent\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"strconv\"\n\t\"time\"\n\n\tcls \"github.com/tencentcloud/tencentcloud-cls-sdk-go\"\n\t\"google.golang.org/protobuf/proto\"\n\n\t\"github.com/go-kratos/kratos/v2/log\"\n)\n\ntype Logger interface {\n\tlog.Logger\n\n\tGetProducer() *cls.AsyncProducerClient\n\tClose() error\n}\n\ntype tencentLog struct {\n\tproducer *cls.AsyncProducerClient\n\topts     *options\n}\n\nfunc (log *tencentLog) GetProducer() *cls.AsyncProducerClient {\n\treturn log.producer\n}\n\ntype options struct {\n\ttopicID      string\n\taccessKey    string\n\taccessSecret string\n\tendpoint     string\n}\n\nfunc defaultOptions() *options {\n\treturn &options{}\n}\n\nfunc WithEndpoint(endpoint string) Option {\n\treturn func(cls *options) {\n\t\tcls.endpoint = endpoint\n\t}\n}\n\nfunc WithTopicID(topicID string) Option {\n\treturn func(cls *options) {\n\t\tcls.topicID = topicID\n\t}\n}\n\nfunc WithAccessKey(ak string) Option {\n\treturn func(cls *options) {\n\t\tcls.accessKey = ak\n\t}\n}\n\nfunc WithAccessSecret(as string) Option {\n\treturn func(cls *options) {\n\t\tcls.accessSecret = as\n\t}\n}\n\ntype Option func(cls *options)\n\nfunc (log *tencentLog) Close() error {\n\treturn log.producer.Close(5000)\n}\n\nfunc (log *tencentLog) Log(level log.Level, keyvals ...any) error {\n\tcontents := make([]*cls.Log_Content, 0, len(keyvals)/2+1)\n\n\tcontents = append(contents, &cls.Log_Content{\n\t\tKey:   newString(level.Key()),\n\t\tValue: newString(level.String()),\n\t})\n\tfor i := 0; i < len(keyvals); i += 2 {\n\t\tcontents = append(contents, &cls.Log_Content{\n\t\t\tKey:   newString(toString(keyvals[i])),\n\t\t\tValue: newString(toString(keyvals[i+1])),\n\t\t})\n\t}\n\n\tlogInst := &cls.Log{\n\t\tTime:     proto.Int64(time.Now().Unix()),\n\t\tContents: contents,\n\t}\n\treturn log.producer.SendLog(log.opts.topicID, logInst, nil)\n}\n\nfunc NewLogger(options ...Option) (Logger, error) {\n\topts := defaultOptions()\n\tfor _, o := range options {\n\t\to(opts)\n\t}\n\tproducerConfig := cls.GetDefaultAsyncProducerClientConfig()\n\tproducerConfig.AccessKeyID = opts.accessKey\n\tproducerConfig.AccessKeySecret = opts.accessSecret\n\tproducerConfig.Endpoint = opts.endpoint\n\tproducerInst, err := cls.NewAsyncProducerClient(producerConfig)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tproducerInst.Start()\n\treturn &tencentLog{\n\t\tproducer: producerInst,\n\t\topts:     opts,\n\t}, nil\n}\n\nfunc newString(s string) *string {\n\treturn &s\n}\n\n// toString convert any type to string\nfunc toString(v any) string {\n\tvar key string\n\tif v == nil {\n\t\treturn key\n\t}\n\tswitch v := v.(type) {\n\tcase float64:\n\t\tkey = strconv.FormatFloat(v, 'f', -1, 64)\n\tcase float32:\n\t\tkey = strconv.FormatFloat(float64(v), 'f', -1, 32)\n\tcase int:\n\t\tkey = strconv.Itoa(v)\n\tcase uint:\n\t\tkey = strconv.FormatUint(uint64(v), 10)\n\tcase int8:\n\t\tkey = strconv.Itoa(int(v))\n\tcase uint8:\n\t\tkey = strconv.FormatUint(uint64(v), 10)\n\tcase int16:\n\t\tkey = strconv.Itoa(int(v))\n\tcase uint16:\n\t\tkey = strconv.FormatUint(uint64(v), 10)\n\tcase int32:\n\t\tkey = strconv.Itoa(int(v))\n\tcase uint32:\n\t\tkey = strconv.FormatUint(uint64(v), 10)\n\tcase int64:\n\t\tkey = strconv.FormatInt(v, 10)\n\tcase uint64:\n\t\tkey = strconv.FormatUint(v, 10)\n\tcase string:\n\t\tkey = v\n\tcase bool:\n\t\tkey = strconv.FormatBool(v)\n\tcase []byte:\n\t\tkey = string(v)\n\tcase fmt.Stringer:\n\t\tkey = v.String()\n\tdefault:\n\t\tnewValue, _ := json.Marshal(v)\n\t\tkey = string(newValue)\n\t}\n\treturn key\n}\n"
  },
  {
    "path": "contrib/log/tencent/tencent_test.go",
    "content": "package tencent\n\nimport (\n\t\"math\"\n\t\"reflect\"\n\t\"testing\"\n\n\t\"github.com/go-kratos/kratos/v2/log\"\n)\n\nfunc TestWithEndpoint(t *testing.T) {\n\topts := new(options)\n\tendpoint := \"eee\"\n\tfuncEndpoint := WithEndpoint(endpoint)\n\tfuncEndpoint(opts)\n\tif opts.endpoint != \"eee\" {\n\t\tt.Errorf(\"WithEndpoint() = %s, want %s\", opts.endpoint, endpoint)\n\t}\n}\n\nfunc TestWithTopicId(t *testing.T) {\n\topts := new(options)\n\ttopicID := \"ee\"\n\tfuncTopicID := WithTopicID(topicID)\n\tfuncTopicID(opts)\n\tif opts.topicID != \"ee\" {\n\t\tt.Errorf(\"WithTopicId() = %s, want %s\", opts.endpoint, topicID)\n\t}\n}\n\nfunc TestWithAccessKey(t *testing.T) {\n\topts := new(options)\n\taccessKey := \"ee\"\n\tfuncAccessKey := WithAccessKey(accessKey)\n\tfuncAccessKey(opts)\n\tif opts.accessKey != \"ee\" {\n\t\tt.Errorf(\"WithAccessKey() = %s, want %s\", opts.endpoint, accessKey)\n\t}\n}\n\nfunc TestWithAccessSecret(t *testing.T) {\n\topts := new(options)\n\taccessSecret := \"ee\"\n\tfuncAccessSecret := WithAccessSecret(accessSecret)\n\tfuncAccessSecret(opts)\n\tif opts.accessSecret != \"ee\" {\n\t\tt.Errorf(\"WithAccessSecret() = %s, want %s\", opts.accessSecret, accessSecret)\n\t}\n}\n\nfunc TestTestLogger(t *testing.T) {\n\ttopicID := \"aa\"\n\tlogger, err := NewLogger(\n\t\tWithTopicID(topicID),\n\t\tWithEndpoint(\"ap-shanghai.cls.tencentcs.com\"),\n\t\tWithAccessKey(\"a\"),\n\t\tWithAccessSecret(\"b\"),\n\t)\n\tif err != nil {\n\t\tt.Error(err)\n\t\treturn\n\t}\n\tdefer logger.Close()\n\tlogger.GetProducer()\n\tflog := log.NewHelper(logger)\n\tflog.Debug(\"log\", \"test\")\n\tflog.Info(\"log\", \"test\")\n\tflog.Warn(\"log\", \"test\")\n\tflog.Error(\"log\", \"test\")\n}\n\nfunc TestLog(t *testing.T) {\n\ttopicID := \"foo\"\n\tlogger, err := NewLogger(\n\t\tWithTopicID(topicID),\n\t\tWithEndpoint(\"ap-shanghai.cls.tencentcs.com\"),\n\t\tWithAccessKey(\"a\"),\n\t\tWithAccessSecret(\"b\"),\n\t)\n\tif err != nil {\n\t\tt.Error(err)\n\t\treturn\n\t}\n\tdefer logger.Close()\n\terr = logger.Log(log.LevelDebug, 0, int8(1), int16(2), int32(3))\n\tif err != nil {\n\t\tt.Errorf(\"Log() returns error:%v\", err)\n\t}\n\terr = logger.Log(log.LevelDebug, uint(0), uint8(1), uint16(2), uint32(3))\n\tif err != nil {\n\t\tt.Errorf(\"Log() returns error:%v\", err)\n\t}\n\terr = logger.Log(log.LevelDebug, uint(0), uint8(1), uint16(2), uint32(3))\n\tif err != nil {\n\t\tt.Errorf(\"Log() returns error:%v\", err)\n\t}\n\terr = logger.Log(log.LevelDebug, int64(0), uint64(1), float32(2), float64(3))\n\tif err != nil {\n\t\tt.Errorf(\"Log() returns error:%v\", err)\n\t}\n\terr = logger.Log(log.LevelDebug, []byte{0, 1, 2, 3}, \"foo\")\n\tif err != nil {\n\t\tt.Errorf(\"Log() returns error:%v\", err)\n\t}\n}\n\nfunc TestNewString(t *testing.T) {\n\tptr := newString(\"\")\n\tif kind := reflect.TypeOf(ptr).Kind(); kind != reflect.Ptr {\n\t\tt.Errorf(\"want type: %v, got type: %v\", reflect.Ptr, kind)\n\t}\n}\n\nfunc TestToString(t *testing.T) {\n\ttests := []struct {\n\t\tname string\n\t\tin   any\n\t\tout  string\n\t}{\n\t\t{\"float64\", 6.66, \"6.66\"},\n\t\t{\"max float64\", math.MaxFloat64, \"179769313486231570000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\"}, //nolint:lll\n\t\t{\"float32\", float32(6.66), \"6.66\"},\n\t\t{\"max float32\", float32(math.MaxFloat32), \"340282350000000000000000000000000000000\"},\n\t\t{\"int\", math.MaxInt64, \"9223372036854775807\"},\n\t\t{\"uint\", uint(math.MaxUint64), \"18446744073709551615\"},\n\t\t{\"int8\", int8(math.MaxInt8), \"127\"},\n\t\t{\"uint8\", uint8(math.MaxUint8), \"255\"},\n\t\t{\"int16\", int16(math.MaxInt16), \"32767\"},\n\t\t{\"uint16\", uint16(math.MaxUint16), \"65535\"},\n\t\t{\"int32\", int32(math.MaxInt32), \"2147483647\"},\n\t\t{\"uint32\", uint32(math.MaxUint32), \"4294967295\"},\n\t\t{\"int64\", int64(math.MaxInt64), \"9223372036854775807\"},\n\t\t{\"uint64\", uint64(math.MaxUint64), \"18446744073709551615\"},\n\t\t{\"string\", \"abc\", \"abc\"},\n\t\t{\"bool\", false, \"false\"},\n\t\t{\"[]byte\", []byte(\"abc\"), \"abc\"},\n\t\t{\"struct\", struct{ Name string }{}, `{\"Name\":\"\"}`},\n\t\t{\"nil\", nil, \"\"},\n\t}\n\tfor _, test := range tests {\n\t\tt.Run(test.name, func(t *testing.T) {\n\t\t\tout := toString(test.in)\n\t\t\tif test.out != out {\n\t\t\t\tt.Fatalf(\"want: %s, got: %s\", test.out, out)\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "contrib/log/zap/go.mod",
    "content": "module github.com/go-kratos/kratos/contrib/log/zap/v2\n\ngo 1.22\n\nrequire (\n\tgithub.com/go-kratos/kratos/v2 v2.9.2\n\tgo.uber.org/zap v1.26.0\n)\n\nrequire go.uber.org/multierr v1.11.0 // indirect\n\nreplace github.com/go-kratos/kratos/v2 => ../../../\n"
  },
  {
    "path": "contrib/log/zap/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/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=\ngithub.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=\ngithub.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=\ngithub.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=\ngo.uber.org/goleak v1.2.0 h1:xqgm/S+aQvhWFTtR0XK3Jvg7z8kGV8P4X14IzwN3Eqk=\ngo.uber.org/goleak v1.2.0/go.mod h1:XJYK+MuIchqpmGmUSAzotztawfKvYLUIgg7guXrwVUo=\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.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo=\ngo.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so=\ngolang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=\ngolang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=\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": "contrib/log/zap/zap.go",
    "content": "package zap\n\nimport (\n\t\"fmt\"\n\n\t\"go.uber.org/zap\"\n\t\"go.uber.org/zap/zapcore\"\n\n\t\"github.com/go-kratos/kratos/v2/log\"\n)\n\nvar _ log.Logger = (*Logger)(nil)\n\ntype Logger struct {\n\tlog    *zap.Logger\n\tmsgKey string\n}\n\ntype Option func(*Logger)\n\n// WithMessageKey with message key.\nfunc WithMessageKey(key string) Option {\n\treturn func(l *Logger) {\n\t\tl.msgKey = key\n\t}\n}\n\nfunc NewLogger(zlog *zap.Logger) *Logger {\n\treturn &Logger{\n\t\tlog:    zlog,\n\t\tmsgKey: log.DefaultMessageKey,\n\t}\n}\n\nfunc (l *Logger) Log(level log.Level, keyvals ...any) error {\n\t// If logging at this level is completely disabled, skip the overhead of\n\t// string formatting.\n\tif zapcore.Level(level) < zapcore.DPanicLevel && !l.log.Core().Enabled(zapcore.Level(level)) {\n\t\treturn nil\n\t}\n\tvar (\n\t\tmsg    = \"\"\n\t\tkeylen = len(keyvals)\n\t)\n\tif keylen == 0 || keylen%2 != 0 {\n\t\tl.log.Warn(fmt.Sprint(\"Keyvalues must appear in pairs: \", keyvals))\n\t\treturn nil\n\t}\n\n\tdata := make([]zap.Field, 0, (keylen/2)+1)\n\tfor i := 0; i < keylen; i += 2 {\n\t\tif keyvals[i].(string) == l.msgKey {\n\t\t\tmsg, _ = keyvals[i+1].(string)\n\t\t\tcontinue\n\t\t}\n\t\tdata = append(data, zap.Any(fmt.Sprint(keyvals[i]), keyvals[i+1]))\n\t}\n\n\tswitch level {\n\tcase log.LevelDebug:\n\t\tl.log.Debug(msg, data...)\n\tcase log.LevelInfo:\n\t\tl.log.Info(msg, data...)\n\tcase log.LevelWarn:\n\t\tl.log.Warn(msg, data...)\n\tcase log.LevelError:\n\t\tl.log.Error(msg, data...)\n\tcase log.LevelFatal:\n\t\tl.log.Fatal(msg, data...)\n\t}\n\treturn nil\n}\n\nfunc (l *Logger) Sync() error {\n\treturn l.log.Sync()\n}\n\nfunc (l *Logger) Close() error {\n\treturn l.Sync()\n}\n"
  },
  {
    "path": "contrib/log/zap/zap_test.go",
    "content": "package zap\n\nimport (\n\t\"testing\"\n\n\t\"go.uber.org/zap\"\n\t\"go.uber.org/zap/zapcore\"\n\n\t\"github.com/go-kratos/kratos/v2/log\"\n)\n\ntype testWriteSyncer struct {\n\toutput []string\n}\n\nfunc (x *testWriteSyncer) Write(p []byte) (n int, err error) {\n\tx.output = append(x.output, string(p))\n\treturn len(p), nil\n}\n\nfunc (x *testWriteSyncer) Sync() error {\n\treturn nil\n}\n\nfunc TestLogger(t *testing.T) {\n\tsyncer := &testWriteSyncer{}\n\tencoderCfg := zapcore.EncoderConfig{\n\t\tMessageKey:     \"msg\",\n\t\tLevelKey:       \"level\",\n\t\tNameKey:        \"logger\",\n\t\tEncodeLevel:    zapcore.LowercaseLevelEncoder,\n\t\tEncodeTime:     zapcore.ISO8601TimeEncoder,\n\t\tEncodeDuration: zapcore.StringDurationEncoder,\n\t}\n\tcore := zapcore.NewCore(zapcore.NewJSONEncoder(encoderCfg), syncer, zap.DebugLevel)\n\tzlogger := zap.New(core).WithOptions()\n\tlogger := NewLogger(zlogger)\n\n\tdefer func() { _ = logger.Close() }()\n\n\tzlog := log.NewHelper(logger)\n\n\tzlog.Debugw(\"log\", \"debug\")\n\tzlog.Infow(\"log\", \"info\")\n\tzlog.Warnw(\"log\", \"warn\")\n\tzlog.Errorw(\"log\", \"error\")\n\tzlog.Errorw(\"log\", \"error\", \"except warn\")\n\tzlog.Info(\"hello world\")\n\n\texcept := []string{\n\t\t\"{\\\"level\\\":\\\"debug\\\",\\\"msg\\\":\\\"\\\",\\\"log\\\":\\\"debug\\\"}\\n\",\n\t\t\"{\\\"level\\\":\\\"info\\\",\\\"msg\\\":\\\"\\\",\\\"log\\\":\\\"info\\\"}\\n\",\n\t\t\"{\\\"level\\\":\\\"warn\\\",\\\"msg\\\":\\\"\\\",\\\"log\\\":\\\"warn\\\"}\\n\",\n\t\t\"{\\\"level\\\":\\\"error\\\",\\\"msg\\\":\\\"\\\",\\\"log\\\":\\\"error\\\"}\\n\",\n\t\t\"{\\\"level\\\":\\\"warn\\\",\\\"msg\\\":\\\"Keyvalues must appear in pairs: [log error except warn]\\\"}\\n\",\n\t\t\"{\\\"level\\\":\\\"info\\\",\\\"msg\\\":\\\"hello world\\\"}\\n\", // not {\"level\":\"info\",\"msg\":\"\",\"msg\":\"hello world\"}\n\t}\n\tfor i, s := range except {\n\t\tif s != syncer.output[i] {\n\t\t\tt.Logf(\"except=%s, got=%s\", s, syncer.output[i])\n\t\t\tt.Fail()\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "contrib/log/zerolog/go.mod",
    "content": "module github.com/go-kratos/kratos/contrib/log/zerolog/v2\n\ngo 1.22\n\nrequire (\n\tgithub.com/go-kratos/kratos/v2 v2.9.2\n\tgithub.com/rs/zerolog v1.30.0\n)\n\nrequire (\n\tgithub.com/mattn/go-colorable v0.1.12 // indirect\n\tgithub.com/mattn/go-isatty v0.0.14 // indirect\n\tgolang.org/x/sys v0.28.0 // indirect\n)\n\nreplace github.com/go-kratos/kratos/v2 => ../../../\n"
  },
  {
    "path": "contrib/log/zerolog/go.sum",
    "content": "github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=\ngithub.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=\ngithub.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40=\ngithub.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=\ngithub.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=\ngithub.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=\ngithub.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=\ngithub.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=\ngithub.com/rs/zerolog v1.30.0 h1:SymVODrcRsaRaSInD9yQtKbtWqwsfoPcRff/oRXLj4c=\ngithub.com/rs/zerolog v1.30.0/go.mod h1:/tk+P47gFdPXq4QYjvCmT5/Gsug2nagsFWBWhAiSi1w=\ngolang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=\ngolang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=\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.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=\ngolang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=\n"
  },
  {
    "path": "contrib/log/zerolog/zerolog.go",
    "content": "package zerolog\n\nimport (\n\t\"github.com/rs/zerolog\"\n\n\t\"github.com/go-kratos/kratos/v2/log\"\n)\n\nvar _ log.Logger = (*Logger)(nil)\n\ntype Logger struct {\n\tlog *zerolog.Logger\n}\n\nfunc NewLogger(logger *zerolog.Logger) log.Logger {\n\treturn &Logger{\n\t\tlog: logger,\n\t}\n}\n\nfunc (l *Logger) Log(level log.Level, keyvals ...any) (err error) {\n\tvar event *zerolog.Event\n\tif len(keyvals) == 0 {\n\t\treturn nil\n\t}\n\tif len(keyvals)%2 != 0 {\n\t\tkeyvals = append(keyvals, \"\")\n\t}\n\n\tswitch level {\n\tcase log.LevelDebug:\n\t\tevent = l.log.Debug()\n\tcase log.LevelInfo:\n\t\tevent = l.log.Info()\n\tcase log.LevelWarn:\n\t\tevent = l.log.Warn()\n\tcase log.LevelError:\n\t\tevent = l.log.Error()\n\tcase log.LevelFatal:\n\t\tevent = l.log.Fatal()\n\tdefault:\n\t\tevent = l.log.Debug()\n\t}\n\n\tfor i := 0; i < len(keyvals); i += 2 {\n\t\tkey, ok := keyvals[i].(string)\n\t\tif !ok {\n\t\t\tcontinue\n\t\t}\n\t\tevent = event.Any(key, keyvals[i+1])\n\t}\n\tevent.Send()\n\treturn\n}\n"
  },
  {
    "path": "contrib/log/zerolog/zerolog_test.go",
    "content": "package zerolog\n\nimport (\n\t\"testing\"\n\n\t\"github.com/rs/zerolog\"\n\n\t\"github.com/go-kratos/kratos/v2/log\"\n)\n\ntype testWriteSyncer struct {\n\toutput []string\n}\n\nfunc (x *testWriteSyncer) Write(p []byte) (n int, err error) {\n\tx.output = append(x.output, string(p))\n\treturn len(p), nil\n}\n\nfunc TestLogger(t *testing.T) {\n\tsyncer := &testWriteSyncer{}\n\tzerolog.TimeFieldFormat = \"2006-01-02 15:04:05.000\"\n\tzlogger := zerolog.New(syncer)\n\tlogger := NewLogger(&zlogger)\n\n\tklog := log.NewHelper(logger)\n\n\tklog.Debugw(\"log\", \"debug\")\n\tklog.Infow(\"log\", \"info\")\n\tklog.Warnw(\"log\", \"warn\")\n\tklog.Errorw(\"log\", \"error\")\n\n\texcept := []string{\n\t\t\"{\\\"level\\\":\\\"debug\\\",\\\"log\\\":\\\"debug\\\"}\\n\",\n\t\t\"{\\\"level\\\":\\\"info\\\",\\\"log\\\":\\\"info\\\"}\\n\",\n\t\t\"{\\\"level\\\":\\\"warn\\\",\\\"log\\\":\\\"warn\\\"}\\n\",\n\t\t\"{\\\"level\\\":\\\"error\\\",\\\"log\\\":\\\"error\\\"}\\n\",\n\t}\n\tfor i, s := range except {\n\t\tif s != syncer.output[i] {\n\t\t\tt.Logf(\"except=%s, got=%s\", s, syncer.output[i])\n\t\t\tt.Fail()\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "contrib/middleware/validate/README.md",
    "content": "# Validator Middleware for Kratos Project\n\nThis module provides a middleware for Kratos to validate request parameters, using schema defined in `.proto` files.\n\nThere used to be a middleware named `Validator` in Kratos, which calls the generated validation functions\nfrom [PGV](https://github.com/bufbuild/protoc-gen-validate) at runtime. Since PGV has been\nin [maintenance](https://github.com/bufbuild/protoc-gen-validate/commit/4a8ffc4942463929c4289407cd4b8c8328ff5422), and\nrecommend using [protovalidate](https://github.com/bufbuild/protovalidate) as an alternative.\n\nThat's why we provide a new middleware that uses the schema definitions and validation functions provided by\nprotovalidate.\n\nprotovalidate no longer requires code generation at build time, but for compatibility with existing Kratos\nprojects, we enable the legacy mode of protovalidate. For most users, no changes are needed to existing code. **But for\nusers who have manually implemented the Validator interface, you need to migrate the relevant implementation yourself**.\n"
  },
  {
    "path": "contrib/middleware/validate/go.mod",
    "content": "module github.com/go-kratos/kratos/contrib/middleware/validate/v2\n\ngo 1.23.0\n\ntoolchain go1.24.6\n\nrequire (\n\tbuf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.6-20250717165733-d22d418d82d8.1\n\tgithub.com/envoyproxy/protoc-gen-validate v1.2.1\n\tgithub.com/go-kratos/kratos/v2 v2.9.2\n\tgoogle.golang.org/protobuf v1.36.6\n)\n\nrequire (\n\tbuf.build/go/protovalidate v0.14.0\n\tcel.dev/expr v0.23.1 // indirect\n\tgithub.com/antlr4-go/antlr/v4 v4.13.1 // indirect\n\tgithub.com/google/cel-go v0.25.0 // indirect\n\tgithub.com/stoewer/go-strcase v1.3.0 // indirect\n\tgolang.org/x/exp v0.0.0-20250305212735-054e65f0b394 // indirect\n\tgolang.org/x/sys v0.31.0 // indirect\n\tgolang.org/x/text v0.23.0 // indirect\n\tgoogle.golang.org/genproto/googleapis/api v0.0.0-20250313205543-e70fdf4c4cb4 // indirect\n\tgoogle.golang.org/genproto/googleapis/rpc v0.0.0-20250313205543-e70fdf4c4cb4 // indirect\n\tgoogle.golang.org/grpc v1.67.1 // indirect\n)\n\nreplace github.com/go-kratos/kratos/v2 => ../../../\n"
  },
  {
    "path": "contrib/middleware/validate/go.sum",
    "content": "buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.6-20250717165733-d22d418d82d8.1 h1:VahIvw/JagkamVOb0q87Az0zu2tmrzlqvO2IKIGOwnI=\nbuf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.6-20250717165733-d22d418d82d8.1/go.mod h1:avRlCjnFzl98VPaeCtJ24RrV/wwHFzB8sWXhj26+n/U=\nbuf.build/go/protovalidate v0.14.0 h1:kr/rC/no+DtRyYX+8KXLDxNnI1rINz0imk5K44ZpZ3A=\nbuf.build/go/protovalidate v0.14.0/go.mod h1:+F/oISho9MO7gJQNYC2VWLzcO1fTPmaTA08SDYJZncA=\ncel.dev/expr v0.23.1 h1:K4KOtPCJQjVggkARsjG9RWXP6O4R73aHeJMa/dmCQQg=\ncel.dev/expr v0.23.1/go.mod h1:hLPLo1W4QUmuYdA72RBX06QTs6MXw941piREPl3Yfiw=\ngithub.com/antlr4-go/antlr/v4 v4.13.1 h1:SqQKkuVZ+zWkMMNkjy5FZe5mr5WURWnlpmOuzYWrPrQ=\ngithub.com/antlr4-go/antlr/v4 v4.13.1/go.mod h1:GKmUxMtwp6ZgGwZSva4eWPC5mS6vUAmOABFgjdkM7Nw=\ngithub.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.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/envoyproxy/protoc-gen-validate v1.2.1 h1:DEo3O99U8j4hBFwbJfrz9VtgcDfUKS7KJ7spH3d86P8=\ngithub.com/envoyproxy/protoc-gen-validate v1.2.1/go.mod h1:d/C80l/jxXLdfEIhX1W2TmLfsJ31lvEjwamM4DxlWXU=\ngithub.com/google/cel-go v0.25.0 h1:jsFw9Fhn+3y2kBbltZR4VEz5xKkcIFRPDnuEzAGv5GY=\ngithub.com/google/cel-go v0.25.0/go.mod h1:hjEb6r5SuOSlhCHmFoLzu8HGCERvIsDAbxDAyNU/MmI=\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/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=\ngithub.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=\ngithub.com/stoewer/go-strcase v1.3.0 h1:g0eASXYtp+yvN9fK8sH94oCIk0fau9uV1/ZdJ0AVEzs=\ngithub.com/stoewer/go-strcase v1.3.0/go.mod h1:fAH5hQ5pehh+j3nZfvwdk2RgEgQjAoM8wodgtPmh1xo=\ngithub.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\ngithub.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=\ngithub.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=\ngithub.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngithub.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=\ngithub.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=\ngithub.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=\ngithub.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=\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/net v0.37.0 h1:1zLorHbz+LYj7MQlSf1+2tPIIgibq2eL5xkrGk6f+2c=\ngolang.org/x/net v0.37.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=\ngolang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=\ngolang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=\ngolang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=\ngolang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=\ngoogle.golang.org/genproto/googleapis/api v0.0.0-20250313205543-e70fdf4c4cb4 h1:IFnXJq3UPB3oBREOodn1v1aGQeZYQclEmvWRMN0PSsY=\ngoogle.golang.org/genproto/googleapis/api v0.0.0-20250313205543-e70fdf4c4cb4/go.mod h1:c8q6Z6OCqnfVIqUFJkCzKcrj8eCvUrz+K4KRzSTuANg=\ngoogle.golang.org/genproto/googleapis/rpc v0.0.0-20250313205543-e70fdf4c4cb4 h1:iK2jbkWL86DXjEx0qiHcRE9dE4/Ahua5k6V8OWFb//c=\ngoogle.golang.org/genproto/googleapis/rpc v0.0.0-20250313205543-e70fdf4c4cb4/go.mod h1:LuRYeWDFV6WOn90g357N17oMCaxpgCnbi/44qJvDn2I=\ngoogle.golang.org/grpc v1.67.1 h1:zWnc1Vrcno+lHZCOofnIMvycFcc0QRGIzm9dhnDX68E=\ngoogle.golang.org/grpc v1.67.1/go.mod h1:1gLDyUQU7CTLJI90u3nXZ9ekeghjeM7pTDZlqFNg2AA=\ngoogle.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=\ngoogle.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=\ngopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\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": "contrib/middleware/validate/internal/testdata/generate.go",
    "content": "package testdata\n\n//go:generate protoc -I . -I ../../../../../third_party --go_out=paths=source_relative:. --validate_out=paths=source_relative,lang=go:. ./test.proto\n"
  },
  {
    "path": "contrib/middleware/validate/internal/testdata/test.pb.go",
    "content": "// Code generated by protoc-gen-go. DO NOT EDIT.\n// versions:\n// \tprotoc-gen-go v1.36.4\n// \tprotoc        v5.29.3\n// source: test.proto\n\npackage testdata\n\nimport (\n\t_ \"buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go/buf/validate\"\n\t_ \"github.com/envoyproxy/protoc-gen-validate/validate\"\n\tprotoreflect \"google.golang.org/protobuf/reflect/protoreflect\"\n\tprotoimpl \"google.golang.org/protobuf/runtime/protoimpl\"\n\treflect \"reflect\"\n\tsync \"sync\"\n\tunsafe \"unsafe\"\n)\n\nconst (\n\t// Verify that this generated code is sufficiently up-to-date.\n\t_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)\n\t// Verify that runtime/protoimpl is sufficiently up-to-date.\n\t_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)\n)\n\ntype Legacy struct {\n\tstate         protoimpl.MessageState `protogen:\"open.v1\"`\n\tName          string                 `protobuf:\"bytes,1,opt,name=name,proto3\" json:\"name,omitempty\"`\n\tAge           int32                  `protobuf:\"varint,2,opt,name=age,proto3\" json:\"age,omitempty\"`\n\tunknownFields protoimpl.UnknownFields\n\tsizeCache     protoimpl.SizeCache\n}\n\nfunc (x *Legacy) Reset() {\n\t*x = Legacy{}\n\tmi := &file_test_proto_msgTypes[0]\n\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\tms.StoreMessageInfo(mi)\n}\n\nfunc (x *Legacy) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*Legacy) ProtoMessage() {}\n\nfunc (x *Legacy) ProtoReflect() protoreflect.Message {\n\tmi := &file_test_proto_msgTypes[0]\n\tif x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use Legacy.ProtoReflect.Descriptor instead.\nfunc (*Legacy) Descriptor() ([]byte, []int) {\n\treturn file_test_proto_rawDescGZIP(), []int{0}\n}\n\nfunc (x *Legacy) GetName() string {\n\tif x != nil {\n\t\treturn x.Name\n\t}\n\treturn \"\"\n}\n\nfunc (x *Legacy) GetAge() int32 {\n\tif x != nil {\n\t\treturn x.Age\n\t}\n\treturn 0\n}\n\ntype Mixed struct {\n\tstate         protoimpl.MessageState `protogen:\"open.v1\"`\n\tName          string                 `protobuf:\"bytes,1,opt,name=name,proto3\" json:\"name,omitempty\"`\n\tAge           int32                  `protobuf:\"varint,2,opt,name=age,proto3\" json:\"age,omitempty\"`\n\tunknownFields protoimpl.UnknownFields\n\tsizeCache     protoimpl.SizeCache\n}\n\nfunc (x *Mixed) Reset() {\n\t*x = Mixed{}\n\tmi := &file_test_proto_msgTypes[1]\n\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\tms.StoreMessageInfo(mi)\n}\n\nfunc (x *Mixed) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*Mixed) ProtoMessage() {}\n\nfunc (x *Mixed) ProtoReflect() protoreflect.Message {\n\tmi := &file_test_proto_msgTypes[1]\n\tif x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use Mixed.ProtoReflect.Descriptor instead.\nfunc (*Mixed) Descriptor() ([]byte, []int) {\n\treturn file_test_proto_rawDescGZIP(), []int{1}\n}\n\nfunc (x *Mixed) GetName() string {\n\tif x != nil {\n\t\treturn x.Name\n\t}\n\treturn \"\"\n}\n\nfunc (x *Mixed) GetAge() int32 {\n\tif x != nil {\n\t\treturn x.Age\n\t}\n\treturn 0\n}\n\ntype Modern struct {\n\tstate         protoimpl.MessageState `protogen:\"open.v1\"`\n\tName          string                 `protobuf:\"bytes,1,opt,name=name,proto3\" json:\"name,omitempty\"`\n\tAge           int32                  `protobuf:\"varint,2,opt,name=age,proto3\" json:\"age,omitempty\"`\n\tunknownFields protoimpl.UnknownFields\n\tsizeCache     protoimpl.SizeCache\n}\n\nfunc (x *Modern) Reset() {\n\t*x = Modern{}\n\tmi := &file_test_proto_msgTypes[2]\n\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\tms.StoreMessageInfo(mi)\n}\n\nfunc (x *Modern) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*Modern) ProtoMessage() {}\n\nfunc (x *Modern) ProtoReflect() protoreflect.Message {\n\tmi := &file_test_proto_msgTypes[2]\n\tif x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use Modern.ProtoReflect.Descriptor instead.\nfunc (*Modern) Descriptor() ([]byte, []int) {\n\treturn file_test_proto_rawDescGZIP(), []int{2}\n}\n\nfunc (x *Modern) GetName() string {\n\tif x != nil {\n\t\treturn x.Name\n\t}\n\treturn \"\"\n}\n\nfunc (x *Modern) GetAge() int32 {\n\tif x != nil {\n\t\treturn x.Age\n\t}\n\treturn 0\n}\n\nvar File_test_proto protoreflect.FileDescriptor\n\nvar file_test_proto_rawDesc = string([]byte{\n\t0x0a, 0x0a, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x08, 0x74, 0x65,\n\t0x73, 0x74, 0x64, 0x61, 0x74, 0x61, 0x1a, 0x1b, 0x62, 0x75, 0x66, 0x2f, 0x76, 0x61, 0x6c, 0x69,\n\t0x64, 0x61, 0x74, 0x65, 0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x72,\n\t0x6f, 0x74, 0x6f, 0x1a, 0x17, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x2f, 0x76, 0x61,\n\t0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x40, 0x0a, 0x06,\n\t0x4c, 0x65, 0x67, 0x61, 0x63, 0x79, 0x12, 0x1b, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01,\n\t0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xfa, 0x42, 0x04, 0x72, 0x02, 0x10, 0x05, 0x52, 0x04, 0x6e,\n\t0x61, 0x6d, 0x65, 0x12, 0x19, 0x0a, 0x03, 0x61, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05,\n\t0x42, 0x07, 0xfa, 0x42, 0x04, 0x1a, 0x02, 0x20, 0x12, 0x52, 0x03, 0x61, 0x67, 0x65, 0x22, 0x3f,\n\t0x0a, 0x05, 0x4d, 0x69, 0x78, 0x65, 0x64, 0x12, 0x1b, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18,\n\t0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x05, 0x52, 0x04,\n\t0x6e, 0x61, 0x6d, 0x65, 0x12, 0x19, 0x0a, 0x03, 0x61, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28,\n\t0x05, 0x42, 0x07, 0xfa, 0x42, 0x04, 0x1a, 0x02, 0x20, 0x12, 0x52, 0x03, 0x61, 0x67, 0x65, 0x22,\n\t0x40, 0x0a, 0x06, 0x4d, 0x6f, 0x64, 0x65, 0x72, 0x6e, 0x12, 0x1b, 0x0a, 0x04, 0x6e, 0x61, 0x6d,\n\t0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x05,\n\t0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x19, 0x0a, 0x03, 0x61, 0x67, 0x65, 0x18, 0x02, 0x20,\n\t0x01, 0x28, 0x05, 0x42, 0x07, 0xba, 0x48, 0x04, 0x1a, 0x02, 0x20, 0x12, 0x52, 0x03, 0x61, 0x67,\n\t0x65, 0x42, 0x4b, 0x5a, 0x49, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f,\n\t0x67, 0x6f, 0x2d, 0x6b, 0x72, 0x61, 0x74, 0x6f, 0x73, 0x2f, 0x6b, 0x72, 0x61, 0x74, 0x6f, 0x73,\n\t0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x69, 0x62, 0x2f, 0x6d, 0x69, 0x64, 0x64, 0x6c, 0x65, 0x77,\n\t0x61, 0x72, 0x65, 0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x2f, 0x69, 0x6e, 0x74,\n\t0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x74, 0x65, 0x73, 0x74, 0x64, 0x61, 0x74, 0x61, 0x62, 0x06,\n\t0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,\n})\n\nvar (\n\tfile_test_proto_rawDescOnce sync.Once\n\tfile_test_proto_rawDescData []byte\n)\n\nfunc file_test_proto_rawDescGZIP() []byte {\n\tfile_test_proto_rawDescOnce.Do(func() {\n\t\tfile_test_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_test_proto_rawDesc), len(file_test_proto_rawDesc)))\n\t})\n\treturn file_test_proto_rawDescData\n}\n\nvar file_test_proto_msgTypes = make([]protoimpl.MessageInfo, 3)\nvar file_test_proto_goTypes = []any{\n\t(*Legacy)(nil), // 0: testdata.Legacy\n\t(*Mixed)(nil),  // 1: testdata.Mixed\n\t(*Modern)(nil), // 2: testdata.Modern\n}\nvar file_test_proto_depIdxs = []int32{\n\t0, // [0:0] is the sub-list for method output_type\n\t0, // [0:0] is the sub-list for method input_type\n\t0, // [0:0] is the sub-list for extension type_name\n\t0, // [0:0] is the sub-list for extension extendee\n\t0, // [0:0] is the sub-list for field type_name\n}\n\nfunc init() { file_test_proto_init() }\nfunc file_test_proto_init() {\n\tif File_test_proto != nil {\n\t\treturn\n\t}\n\ttype x struct{}\n\tout := protoimpl.TypeBuilder{\n\t\tFile: protoimpl.DescBuilder{\n\t\t\tGoPackagePath: reflect.TypeOf(x{}).PkgPath(),\n\t\t\tRawDescriptor: unsafe.Slice(unsafe.StringData(file_test_proto_rawDesc), len(file_test_proto_rawDesc)),\n\t\t\tNumEnums:      0,\n\t\t\tNumMessages:   3,\n\t\t\tNumExtensions: 0,\n\t\t\tNumServices:   0,\n\t\t},\n\t\tGoTypes:           file_test_proto_goTypes,\n\t\tDependencyIndexes: file_test_proto_depIdxs,\n\t\tMessageInfos:      file_test_proto_msgTypes,\n\t}.Build()\n\tFile_test_proto = out.File\n\tfile_test_proto_goTypes = nil\n\tfile_test_proto_depIdxs = nil\n}\n"
  },
  {
    "path": "contrib/middleware/validate/internal/testdata/test.pb.validate.go",
    "content": "// Code generated by protoc-gen-validate. DO NOT EDIT.\n// source: test.proto\n\npackage testdata\n\nimport (\n\t\"bytes\"\n\t\"errors\"\n\t\"fmt\"\n\t\"net\"\n\t\"net/mail\"\n\t\"net/url\"\n\t\"regexp\"\n\t\"sort\"\n\t\"strings\"\n\t\"time\"\n\t\"unicode/utf8\"\n\n\t\"google.golang.org/protobuf/types/known/anypb\"\n)\n\n// ensure the imports are used\nvar (\n\t_ = bytes.MinRead\n\t_ = errors.New(\"\")\n\t_ = fmt.Print\n\t_ = utf8.UTFMax\n\t_ = (*regexp.Regexp)(nil)\n\t_ = (*strings.Reader)(nil)\n\t_ = net.IPv4len\n\t_ = time.Duration(0)\n\t_ = (*url.URL)(nil)\n\t_ = (*mail.Address)(nil)\n\t_ = anypb.Any{}\n\t_ = sort.Sort\n)\n\n// Validate checks the field values on Legacy with the rules defined in the\n// proto definition for this message. If any rules are violated, the first\n// error encountered is returned, or nil if there are no violations.\nfunc (m *Legacy) Validate() error {\n\treturn m.validate(false)\n}\n\n// ValidateAll checks the field values on Legacy with the rules defined in the\n// proto definition for this message. If any rules are violated, the result is\n// a list of violation errors wrapped in LegacyMultiError, or nil if none found.\nfunc (m *Legacy) ValidateAll() error {\n\treturn m.validate(true)\n}\n\nfunc (m *Legacy) validate(all bool) error {\n\tif m == nil {\n\t\treturn nil\n\t}\n\n\tvar errors []error\n\n\tif utf8.RuneCountInString(m.GetName()) < 5 {\n\t\terr := LegacyValidationError{\n\t\t\tfield:  \"Name\",\n\t\t\treason: \"value length must be at least 5 runes\",\n\t\t}\n\t\tif !all {\n\t\t\treturn err\n\t\t}\n\t\terrors = append(errors, err)\n\t}\n\n\tif m.GetAge() <= 18 {\n\t\terr := LegacyValidationError{\n\t\t\tfield:  \"Age\",\n\t\t\treason: \"value must be greater than 18\",\n\t\t}\n\t\tif !all {\n\t\t\treturn err\n\t\t}\n\t\terrors = append(errors, err)\n\t}\n\n\tif len(errors) > 0 {\n\t\treturn LegacyMultiError(errors)\n\t}\n\n\treturn nil\n}\n\n// LegacyMultiError is an error wrapping multiple validation errors returned by\n// Legacy.ValidateAll() if the designated constraints aren't met.\ntype LegacyMultiError []error\n\n// Error returns a concatenation of all the error messages it wraps.\nfunc (m LegacyMultiError) Error() string {\n\tmsgs := make([]string, 0, len(m))\n\tfor _, err := range m {\n\t\tmsgs = append(msgs, err.Error())\n\t}\n\treturn strings.Join(msgs, \"; \")\n}\n\n// AllErrors returns a list of validation violation errors.\nfunc (m LegacyMultiError) AllErrors() []error { return m }\n\n// LegacyValidationError is the validation error returned by Legacy.Validate if\n// the designated constraints aren't met.\ntype LegacyValidationError struct {\n\tfield  string\n\treason string\n\tcause  error\n\tkey    bool\n}\n\n// Field function returns field value.\nfunc (e LegacyValidationError) Field() string { return e.field }\n\n// Reason function returns reason value.\nfunc (e LegacyValidationError) Reason() string { return e.reason }\n\n// Cause function returns cause value.\nfunc (e LegacyValidationError) Cause() error { return e.cause }\n\n// Key function returns key value.\nfunc (e LegacyValidationError) Key() bool { return e.key }\n\n// ErrorName returns error name.\nfunc (e LegacyValidationError) ErrorName() string { return \"LegacyValidationError\" }\n\n// Error satisfies the builtin error interface\nfunc (e LegacyValidationError) Error() string {\n\tcause := \"\"\n\tif e.cause != nil {\n\t\tcause = fmt.Sprintf(\" | caused by: %v\", e.cause)\n\t}\n\n\tkey := \"\"\n\tif e.key {\n\t\tkey = \"key for \"\n\t}\n\n\treturn fmt.Sprintf(\n\t\t\"invalid %sLegacy.%s: %s%s\",\n\t\tkey,\n\t\te.field,\n\t\te.reason,\n\t\tcause)\n}\n\nvar _ error = LegacyValidationError{}\n\nvar _ interface {\n\tField() string\n\tReason() string\n\tKey() bool\n\tCause() error\n\tErrorName() string\n} = LegacyValidationError{}\n\n// Validate checks the field values on Mixed with the rules defined in the\n// proto definition for this message. If any rules are violated, the first\n// error encountered is returned, or nil if there are no violations.\nfunc (m *Mixed) Validate() error {\n\treturn m.validate(false)\n}\n\n// ValidateAll checks the field values on Mixed with the rules defined in the\n// proto definition for this message. If any rules are violated, the result is\n// a list of violation errors wrapped in MixedMultiError, or nil if none found.\nfunc (m *Mixed) ValidateAll() error {\n\treturn m.validate(true)\n}\n\nfunc (m *Mixed) validate(all bool) error {\n\tif m == nil {\n\t\treturn nil\n\t}\n\n\tvar errors []error\n\n\t// no validation rules for Name\n\n\tif m.GetAge() <= 18 {\n\t\terr := MixedValidationError{\n\t\t\tfield:  \"Age\",\n\t\t\treason: \"value must be greater than 18\",\n\t\t}\n\t\tif !all {\n\t\t\treturn err\n\t\t}\n\t\terrors = append(errors, err)\n\t}\n\n\tif len(errors) > 0 {\n\t\treturn MixedMultiError(errors)\n\t}\n\n\treturn nil\n}\n\n// MixedMultiError is an error wrapping multiple validation errors returned by\n// Mixed.ValidateAll() if the designated constraints aren't met.\ntype MixedMultiError []error\n\n// Error returns a concatenation of all the error messages it wraps.\nfunc (m MixedMultiError) Error() string {\n\tmsgs := make([]string, 0, len(m))\n\tfor _, err := range m {\n\t\tmsgs = append(msgs, err.Error())\n\t}\n\treturn strings.Join(msgs, \"; \")\n}\n\n// AllErrors returns a list of validation violation errors.\nfunc (m MixedMultiError) AllErrors() []error { return m }\n\n// MixedValidationError is the validation error returned by Mixed.Validate if\n// the designated constraints aren't met.\ntype MixedValidationError struct {\n\tfield  string\n\treason string\n\tcause  error\n\tkey    bool\n}\n\n// Field function returns field value.\nfunc (e MixedValidationError) Field() string { return e.field }\n\n// Reason function returns reason value.\nfunc (e MixedValidationError) Reason() string { return e.reason }\n\n// Cause function returns cause value.\nfunc (e MixedValidationError) Cause() error { return e.cause }\n\n// Key function returns key value.\nfunc (e MixedValidationError) Key() bool { return e.key }\n\n// ErrorName returns error name.\nfunc (e MixedValidationError) ErrorName() string { return \"MixedValidationError\" }\n\n// Error satisfies the builtin error interface\nfunc (e MixedValidationError) Error() string {\n\tcause := \"\"\n\tif e.cause != nil {\n\t\tcause = fmt.Sprintf(\" | caused by: %v\", e.cause)\n\t}\n\n\tkey := \"\"\n\tif e.key {\n\t\tkey = \"key for \"\n\t}\n\n\treturn fmt.Sprintf(\n\t\t\"invalid %sMixed.%s: %s%s\",\n\t\tkey,\n\t\te.field,\n\t\te.reason,\n\t\tcause)\n}\n\nvar _ error = MixedValidationError{}\n\nvar _ interface {\n\tField() string\n\tReason() string\n\tKey() bool\n\tCause() error\n\tErrorName() string\n} = MixedValidationError{}\n\n// Validate checks the field values on Modern with the rules defined in the\n// proto definition for this message. If any rules are violated, the first\n// error encountered is returned, or nil if there are no violations.\nfunc (m *Modern) Validate() error {\n\treturn m.validate(false)\n}\n\n// ValidateAll checks the field values on Modern with the rules defined in the\n// proto definition for this message. If any rules are violated, the result is\n// a list of violation errors wrapped in ModernMultiError, or nil if none found.\nfunc (m *Modern) ValidateAll() error {\n\treturn m.validate(true)\n}\n\nfunc (m *Modern) validate(all bool) error {\n\tif m == nil {\n\t\treturn nil\n\t}\n\n\tvar errors []error\n\n\t// no validation rules for Name\n\n\t// no validation rules for Age\n\n\tif len(errors) > 0 {\n\t\treturn ModernMultiError(errors)\n\t}\n\n\treturn nil\n}\n\n// ModernMultiError is an error wrapping multiple validation errors returned by\n// Modern.ValidateAll() if the designated constraints aren't met.\ntype ModernMultiError []error\n\n// Error returns a concatenation of all the error messages it wraps.\nfunc (m ModernMultiError) Error() string {\n\tmsgs := make([]string, 0, len(m))\n\tfor _, err := range m {\n\t\tmsgs = append(msgs, err.Error())\n\t}\n\treturn strings.Join(msgs, \"; \")\n}\n\n// AllErrors returns a list of validation violation errors.\nfunc (m ModernMultiError) AllErrors() []error { return m }\n\n// ModernValidationError is the validation error returned by Modern.Validate if\n// the designated constraints aren't met.\ntype ModernValidationError struct {\n\tfield  string\n\treason string\n\tcause  error\n\tkey    bool\n}\n\n// Field function returns field value.\nfunc (e ModernValidationError) Field() string { return e.field }\n\n// Reason function returns reason value.\nfunc (e ModernValidationError) Reason() string { return e.reason }\n\n// Cause function returns cause value.\nfunc (e ModernValidationError) Cause() error { return e.cause }\n\n// Key function returns key value.\nfunc (e ModernValidationError) Key() bool { return e.key }\n\n// ErrorName returns error name.\nfunc (e ModernValidationError) ErrorName() string { return \"ModernValidationError\" }\n\n// Error satisfies the builtin error interface\nfunc (e ModernValidationError) Error() string {\n\tcause := \"\"\n\tif e.cause != nil {\n\t\tcause = fmt.Sprintf(\" | caused by: %v\", e.cause)\n\t}\n\n\tkey := \"\"\n\tif e.key {\n\t\tkey = \"key for \"\n\t}\n\n\treturn fmt.Sprintf(\n\t\t\"invalid %sModern.%s: %s%s\",\n\t\tkey,\n\t\te.field,\n\t\te.reason,\n\t\tcause)\n}\n\nvar _ error = ModernValidationError{}\n\nvar _ interface {\n\tField() string\n\tReason() string\n\tKey() bool\n\tCause() error\n\tErrorName() string\n} = ModernValidationError{}\n"
  },
  {
    "path": "contrib/middleware/validate/internal/testdata/test.proto",
    "content": "syntax = \"proto3\";\n\npackage testdata;\n\nimport \"buf/validate/validate.proto\";\nimport \"validate/validate.proto\";\n\noption go_package = \"github.com/go-kratos/kratos/contrib/middleware/validate/internal/testdata\";\n\nmessage Legacy {\n  string name = 1 [(validate.rules).string.min_len = 5];\n  int32 age = 2 [(validate.rules).int32.gt = 18];\n}\n\nmessage Mixed {\n  string name = 1 [(buf.validate.field).string.min_len = 5];\n  int32 age = 2 [(validate.rules).int32.gt = 18];\n}\n\nmessage Modern {\n  string name = 1 [(buf.validate.field).string.min_len = 5];\n  int32 age = 2 [(buf.validate.field).int32.gt = 18];\n}\n"
  },
  {
    "path": "contrib/middleware/validate/validate.go",
    "content": "package validate\n\nimport (\n\t\"context\"\n\n\t\"github.com/go-kratos/kratos/v2/errors\"\n\t\"github.com/go-kratos/kratos/v2/middleware\"\n\n\t\"buf.build/go/protovalidate\"\n\t\"google.golang.org/protobuf/proto\"\n)\n\ntype validator interface {\n\tValidate() error\n}\n\n// ProtoValidate is a middleware that validates the request message with [protovalidate](https://github.com/bufbuild/protovalidate)\nfunc ProtoValidate() middleware.Middleware {\n\treturn func(handler middleware.Handler) middleware.Handler {\n\t\treturn func(ctx context.Context, req any) (reply any, err error) {\n\t\t\tif msg, ok := req.(proto.Message); ok {\n\t\t\t\tif err := protovalidate.Validate(msg); err != nil {\n\t\t\t\t\treturn nil, errors.BadRequest(\"VALIDATOR\", err.Error()).WithCause(err)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// to compatible with the [old validator](https://github.com/envoyproxy/protoc-gen-validate)\n\t\t\tif v, ok := req.(validator); ok {\n\t\t\t\tif err := v.Validate(); err != nil {\n\t\t\t\t\treturn nil, errors.BadRequest(\"VALIDATOR\", err.Error()).WithCause(err)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn handler(ctx, req)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "contrib/middleware/validate/validate_test.go",
    "content": "package validate\n\nimport (\n\t\"context\"\n\t\"testing\"\n\n\t\"google.golang.org/protobuf/proto\"\n\n\t\"github.com/go-kratos/kratos/contrib/middleware/validate/v2/internal/testdata\"\n\tkerrors \"github.com/go-kratos/kratos/v2/errors\"\n\t\"github.com/go-kratos/kratos/v2/middleware\"\n)\n\ntype testcase struct {\n\tname string\n\treq  proto.Message\n\terr  bool\n}\n\nfunc TestTable(t *testing.T) {\n\tvar mock middleware.Handler = func(context.Context, any) (any, error) { return nil, nil }\n\n\ttests := []testcase{\n\t\t{\n\t\t\tname: \"valid_legacy\",\n\t\t\treq:  &testdata.Legacy{Name: \"testcase\", Age: 19},\n\t\t\terr:  false,\n\t\t},\n\t\t{\n\t\t\tname: \"invalid_legacy1\",\n\t\t\treq:  &testdata.Legacy{Name: \"testcase\", Age: 10},\n\t\t\terr:  true,\n\t\t},\n\t\t{\n\t\t\tname: \"invalid_legacy2\",\n\t\t\treq:  &testdata.Legacy{Name: \"test\", Age: 100},\n\t\t\terr:  true,\n\t\t},\n\t\t{\n\t\t\tname: \"valid_mixed\",\n\t\t\treq:  &testdata.Mixed{Name: \"testcase\", Age: 19},\n\t\t\terr:  false,\n\t\t},\n\t\t{\n\t\t\tname: \"invalid_mixed1\",\n\t\t\treq:  &testdata.Mixed{Name: \"testcase\", Age: 10},\n\t\t\terr:  true,\n\t\t},\n\t\t{\n\t\t\tname: \"invalid_mixed2\",\n\t\t\treq:  &testdata.Mixed{Name: \"test\", Age: 100},\n\t\t\terr:  true,\n\t\t},\n\t\t{\n\t\t\tname: \"valid_modern\",\n\t\t\treq:  &testdata.Modern{Name: \"testcase\", Age: 19},\n\t\t\terr:  false,\n\t\t},\n\t\t{\n\t\t\tname: \"invalid_modern1\",\n\t\t\treq:  &testdata.Modern{Name: \"testcase\", Age: 10},\n\t\t\terr:  true,\n\t\t},\n\t\t{\n\t\t\tname: \"invalid_modern2\",\n\t\t\treq:  &testdata.Modern{Name: \"test\", Age: 100},\n\t\t\terr:  true,\n\t\t},\n\t}\n\n\tfor _, test := range tests {\n\t\tt.Run(test.name, func(t *testing.T) {\n\t\t\thandle := ProtoValidate()(mock)\n\t\t\t_, err := handle(context.Background(), test.req)\n\t\t\texpect := test.err\n\t\t\tactual := kerrors.IsBadRequest(err)\n\t\t\tif expect != actual {\n\t\t\t\tt.Errorf(\"case %s expect %v, actual %v, err %v\", test.name, expect, actual, err)\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "contrib/opensergo/README.md",
    "content": "# OpenSergo\n\n## Usage\n\n```go\nosServer, err := opensergo.New(opensergo.WithEndpoint(\"localhost:9090\"))\nif err != nil {\n\tpanic(\"init opensergo error\")\n}\n\ns := &server{}\ngrpcSrv := grpc.NewServer(\n\tgrpc.Address(\":9000\"),\n\tgrpc.Middleware(\n\t\trecovery.Recovery(),\n\t),\n)\nhelloworld.RegisterGreeterServer(grpcSrv, s)\n\napp := kratos.New(\n\tkratos.Name(Name),\n\tkratos.Server(\n\t\tgrpcSrv,\n\t),\n)\n\nosServer.ReportMetadata(context.Background(), app)\n\nif err := app.Run(); err != nil {\n\tlog.Fatal(err)\n}\n```\n"
  },
  {
    "path": "contrib/opensergo/go.mod",
    "content": "module github.com/go-kratos/kratos/contrib/opensergo/v2\n\ngo 1.22\n\nrequire (\n\tgithub.com/go-kratos/kratos/v2 v2.9.2\n\tgithub.com/opensergo/opensergo-go v0.0.0-20220331070310-e5b01fee4d1c\n\tgoogle.golang.org/genproto/googleapis/api v0.0.0-20240102182953-50ed04b92917\n\tgoogle.golang.org/grpc v1.61.1\n\tgoogle.golang.org/protobuf v1.33.0\n)\n\nrequire (\n\tgithub.com/go-playground/form/v4 v4.2.0 // indirect\n\tgithub.com/golang/protobuf v1.5.4 // indirect\n\tgithub.com/google/uuid v1.4.0 // indirect\n\tgithub.com/kr/text v0.2.0 // indirect\n\tgolang.org/x/net v0.33.0 // indirect\n\tgolang.org/x/sync v0.10.0 // indirect\n\tgolang.org/x/sys v0.28.0 // indirect\n\tgolang.org/x/text v0.21.0 // indirect\n\tgoogle.golang.org/genproto v0.0.0-20231212172506-995d672761c0 // indirect\n\tgoogle.golang.org/genproto/googleapis/rpc v0.0.0-20240102182953-50ed04b92917 // indirect\n\tgopkg.in/yaml.v3 v3.0.1 // indirect\n)\n\nreplace github.com/go-kratos/kratos/v2 => ../../\n"
  },
  {
    "path": "contrib/opensergo/go.sum",
    "content": "cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=\ncloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=\ngithub.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=\ngithub.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=\ngithub.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=\ngithub.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=\ngithub.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=\ngithub.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=\ngithub.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=\ngithub.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI=\ngithub.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=\ngithub.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=\ngithub.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=\ngithub.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=\ngithub.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=\ngithub.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=\ngithub.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=\ngithub.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=\ngithub.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0=\ngithub.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=\ngithub.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=\ngithub.com/go-kratos/aegis v0.2.0 h1:dObzCDWn3XVjUkgxyBp6ZeWtx/do0DPZ7LY3yNSJLUQ=\ngithub.com/go-kratos/aegis v0.2.0/go.mod h1:v0R2m73WgEEYB3XYu6aE2WcMwsZkJ/Rzuf5eVccm7bI=\ngithub.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A=\ngithub.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=\ngithub.com/go-playground/form/v4 v4.2.0 h1:N1wh+Goz61e6w66vo8vJkQt+uwZSoLz50kZPJWR8eic=\ngithub.com/go-playground/form/v4 v4.2.0/go.mod h1:q1a2BY+AQUUzhl6xA/6hBetay6dEIhMHjgvJiGo6K7U=\ngithub.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=\ngithub.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=\ngithub.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=\ngithub.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=\ngithub.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=\ngithub.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=\ngithub.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=\ngithub.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=\ngithub.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=\ngithub.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=\ngithub.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=\ngithub.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=\ngithub.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=\ngithub.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=\ngithub.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=\ngithub.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=\ngithub.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=\ngithub.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=\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.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\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/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=\ngithub.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4=\ngithub.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=\ngithub.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=\ngithub.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=\ngithub.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=\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/opensergo/opensergo-go v0.0.0-20220331070310-e5b01fee4d1c h1:Ivg+PYq7sOf2IwBGbo9Tt6jk+LHSUapWVPBEr9ygh8Q=\ngithub.com/opensergo/opensergo-go v0.0.0-20220331070310-e5b01fee4d1c/go.mod h1:VxL391S2BWXU1m14087xt8+2YTgsnfa+xsSrbuoFKl4=\ngithub.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=\ngithub.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=\ngithub.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=\ngithub.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=\ngithub.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=\ngithub.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\ngithub.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=\ngithub.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngo.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=\ngolang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=\ngolang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=\ngolang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=\ngolang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=\ngolang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=\ngolang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=\ngolang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I=\ngolang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=\ngolang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=\ngolang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=\ngolang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\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.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=\ngolang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=\ngolang.org/x/sys v0.0.0-20180830151530-49385e6e1522/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-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=\ngolang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=\ngolang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=\ngolang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=\ngolang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=\ngolang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=\ngolang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=\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=\ngoogle.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=\ngoogle.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=\ngoogle.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=\ngoogle.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=\ngoogle.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=\ngoogle.golang.org/genproto v0.0.0-20231212172506-995d672761c0 h1:YJ5pD9rF8o9Qtta0Cmy9rdBwkSjrTCT6XTiUQVOtIos=\ngoogle.golang.org/genproto v0.0.0-20231212172506-995d672761c0/go.mod h1:l/k7rMz0vFTBPy+tFSGvXEd3z+BcoG1k7EHbqm+YBsY=\ngoogle.golang.org/genproto/googleapis/api v0.0.0-20240102182953-50ed04b92917 h1:rcS6EyEaoCO52hQDupoSfrxI3R6C2Tq741is7X8OvnM=\ngoogle.golang.org/genproto/googleapis/api v0.0.0-20240102182953-50ed04b92917/go.mod h1:CmlNWB9lSezaYELKS5Ym1r44VrrbPUa7JTvw+6MbpJ0=\ngoogle.golang.org/genproto/googleapis/rpc v0.0.0-20240102182953-50ed04b92917 h1:6G8oQ016D88m1xAKljMlBOOGWDZkes4kMhgGFlf8WcQ=\ngoogle.golang.org/genproto/googleapis/rpc v0.0.0-20240102182953-50ed04b92917/go.mod h1:xtjpI3tXFPP051KaWnhvxkiubL/6dJ18vLVf7q2pTOU=\ngoogle.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=\ngoogle.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=\ngoogle.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=\ngoogle.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=\ngoogle.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=\ngoogle.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=\ngoogle.golang.org/grpc v1.43.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=\ngoogle.golang.org/grpc v1.61.1 h1:kLAiWrZs7YeDM6MumDe7m3y4aM6wacLzM1Y/wiLP9XY=\ngoogle.golang.org/grpc v1.61.1/go.mod h1:VUbo7IFqmF1QtCAstipjG0GIoq49KvMe9+h1jFLBNJs=\ngoogle.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=\ngoogle.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=\ngoogle.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=\ngoogle.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=\ngoogle.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=\ngoogle.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=\ngoogle.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=\ngoogle.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=\ngoogle.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=\ngoogle.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=\ngoogle.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=\ngoogle.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=\ngoogle.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=\ngopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/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.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\ngopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=\ngopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\nhonnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\nhonnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\n"
  },
  {
    "path": "contrib/opensergo/opensergo.go",
    "content": "package opensergo\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"net\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"os\"\n\t\"strconv\"\n\t\"time\"\n\n\tv1 \"github.com/opensergo/opensergo-go/proto/service_contract/v1\"\n\t\"google.golang.org/genproto/googleapis/api/annotations\"\n\t\"google.golang.org/grpc\"\n\t\"google.golang.org/grpc/credentials/insecure\"\n\t\"google.golang.org/protobuf/proto\"\n\t\"google.golang.org/protobuf/reflect/protoreflect\"\n\t\"google.golang.org/protobuf/reflect/protoregistry\"\n\n\t\"github.com/go-kratos/kratos/v2\"\n)\n\ntype Option func(*options)\n\nfunc WithEndpoint(endpoint string) Option {\n\treturn func(o *options) {\n\t\to.Endpoint = endpoint\n\t}\n}\n\ntype options struct {\n\tEndpoint string `json:\"endpoint\"`\n}\n\nfunc (o *options) ParseJSON(data []byte) error {\n\treturn json.Unmarshal(data, o)\n}\n\ntype OpenSergo struct {\n\tmdClient v1.MetadataServiceClient\n}\n\nfunc New(opts ...Option) (*OpenSergo, error) {\n\topt := options{\n\t\tEndpoint: os.Getenv(\"OPENSERGO_ENDPOINT\"),\n\t}\n\t// https://github.com/opensergo/opensergo-specification/blob/main/specification/en/README.md\n\tif v := os.Getenv(\"OPENSERGO_BOOTSTRAP\"); v != \"\" {\n\t\tif err := opt.ParseJSON([]byte(v)); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\tif v := os.Getenv(\"OPENSERGO_BOOTSTRAP_CONFIG\"); v != \"\" {\n\t\tb, err := os.ReadFile(v)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tif err := opt.ParseJSON(b); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\tfor _, o := range opts {\n\t\to(&opt)\n\t}\n\tdialCtx := context.Background()\n\tdialCtx, cancel := context.WithTimeout(dialCtx, time.Second)\n\tdefer cancel()\n\tconn, err := grpc.DialContext(dialCtx, opt.Endpoint, grpc.WithTransportCredentials(insecure.NewCredentials()))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn &OpenSergo{\n\t\tmdClient: v1.NewMetadataServiceClient(conn),\n\t}, nil\n}\n\nfunc (s *OpenSergo) ReportMetadata(ctx context.Context, app kratos.AppInfo) error {\n\tservices, types, err := listDescriptors()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tserviceMetadata := &v1.ServiceMetadata{\n\t\tServiceContract: &v1.ServiceContract{\n\t\t\tServices: services,\n\t\t\tTypes:    types,\n\t\t},\n\t}\n\n\tfor _, endpoint := range app.Endpoint() {\n\t\tu, err := url.Parse(endpoint) // nolint\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\thost, port, err := net.SplitHostPort(u.Host)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tportUint64, err := strconv.ParseUint(port, 10, 32)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tserviceMetadata.Protocols = append(serviceMetadata.Protocols, u.Scheme)\n\t\tserviceMetadata.ListeningAddresses = append(serviceMetadata.ListeningAddresses, &v1.SocketAddress{\n\t\t\tAddress:   host,\n\t\t\tPortValue: uint32(portUint64),\n\t\t})\n\t}\n\t_, err = s.mdClient.ReportMetadata(ctx, &v1.ReportMetadataRequest{\n\t\tAppName:         app.Name(),\n\t\tServiceMetadata: []*v1.ServiceMetadata{serviceMetadata},\n\t\t// TODO: Node: *v1.Node,\n\t})\n\treturn err\n}\n\nfunc listDescriptors() (services []*v1.ServiceDescriptor, types []*v1.TypeDescriptor, err error) {\n\tprotoregistry.GlobalFiles.RangeFiles(func(fd protoreflect.FileDescriptor) bool {\n\t\tfor i := 0; i < fd.Services().Len(); i++ {\n\t\t\tvar (\n\t\t\t\tmethods []*v1.MethodDescriptor\n\t\t\t\tsd      = fd.Services().Get(i)\n\t\t\t)\n\t\t\tfor j := 0; j < sd.Methods().Len(); j++ {\n\t\t\t\tmd := sd.Methods().Get(j)\n\t\t\t\tmName := string(md.Name())\n\t\t\t\tinputType := string(md.Input().FullName())\n\t\t\t\toutputType := string(md.Output().FullName())\n\t\t\t\tisClientStreaming := md.IsStreamingClient()\n\t\t\t\tisServerStreaming := md.IsStreamingServer()\n\t\t\t\tpattern := proto.GetExtension(md.Options(), annotations.E_Http).(*annotations.HttpRule).GetPattern()\n\t\t\t\tvar httpPath, httpMethod string\n\t\t\t\tif pattern != nil {\n\t\t\t\t\thttpMethod, httpPath = HTTPPatternInfo(pattern)\n\t\t\t\t}\n\t\t\t\tmethodDesc := v1.MethodDescriptor{\n\t\t\t\t\tName:            mName,\n\t\t\t\t\tInputTypes:      []string{inputType},\n\t\t\t\t\tOutputTypes:     []string{outputType},\n\t\t\t\t\tClientStreaming: &isClientStreaming,\n\t\t\t\t\tServerStreaming: &isServerStreaming,\n\t\t\t\t\tHttpPaths:       []string{httpPath},\n\t\t\t\t\tHttpMethods:     []string{httpMethod},\n\t\t\t\t\t// TODO: Description: *string,\n\t\t\t\t}\n\t\t\t\tmethods = append(methods, &methodDesc)\n\t\t\t}\n\t\t\tservices = append(services, &v1.ServiceDescriptor{\n\t\t\t\tName:    string(sd.Name()),\n\t\t\t\tMethods: methods,\n\t\t\t\t// TODO: Description: *string,\n\t\t\t})\n\t\t}\n\n\t\tfor i := 0; i < fd.Messages().Len(); i++ {\n\t\t\tvar (\n\t\t\t\tfields []*v1.FieldDescriptor\n\t\t\t\tmd     = fd.Messages().Get(i)\n\t\t\t)\n\n\t\t\tfor j := 0; j < md.Fields().Len(); j++ {\n\t\t\t\tfd := md.Fields().Get(j)\n\t\t\t\tkind := fd.Kind()\n\t\t\t\ttypeName := kind.String()\n\n\t\t\t\tfields = append(fields, &v1.FieldDescriptor{\n\t\t\t\t\tName:     string(fd.Name()),\n\t\t\t\t\tNumber:   int32(fd.Number()),\n\t\t\t\t\tType:     v1.FieldDescriptor_Type(kind),\n\t\t\t\t\tTypeName: &typeName,\n\t\t\t\t\t// TODO: Description: *string,\n\t\t\t\t})\n\t\t\t}\n\n\t\t\ttypes = append(types, &v1.TypeDescriptor{\n\t\t\t\tName:   string(md.Name()),\n\t\t\t\tFields: fields,\n\t\t\t})\n\t\t}\n\n\t\treturn true\n\t})\n\treturn\n}\n\nfunc HTTPPatternInfo(pattern any) (method string, path string) {\n\tswitch p := pattern.(type) {\n\tcase *annotations.HttpRule_Get:\n\t\treturn http.MethodGet, p.Get\n\tcase *annotations.HttpRule_Post:\n\t\treturn http.MethodPost, p.Post\n\tcase *annotations.HttpRule_Delete:\n\t\treturn http.MethodDelete, p.Delete\n\tcase *annotations.HttpRule_Patch:\n\t\treturn http.MethodPatch, p.Patch\n\tcase *annotations.HttpRule_Put:\n\t\treturn http.MethodPut, p.Put\n\tcase *annotations.HttpRule_Custom:\n\t\treturn p.Custom.Kind, p.Custom.Path\n\tdefault:\n\t\treturn \"\", \"\"\n\t}\n}\n"
  },
  {
    "path": "contrib/opensergo/opensergo_test.go",
    "content": "package opensergo\n\nimport (\n\t\"context\"\n\t\"net\"\n\t\"net/http\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"reflect\"\n\t\"testing\"\n\n\tsrvContractPb \"github.com/opensergo/opensergo-go/proto/service_contract/v1\"\n\t\"google.golang.org/genproto/googleapis/api/annotations\"\n\t\"google.golang.org/grpc\"\n\t\"google.golang.org/protobuf/proto\"\n\t\"google.golang.org/protobuf/reflect/protodesc\"\n\tpref \"google.golang.org/protobuf/reflect/protoreflect\"\n\t\"google.golang.org/protobuf/reflect/protoregistry\"\n\t\"google.golang.org/protobuf/types/descriptorpb\"\n)\n\ntype testMetadataServiceServer struct {\n\tsrvContractPb.UnimplementedMetadataServiceServer\n}\n\nfunc (m *testMetadataServiceServer) ReportMetadata(_ context.Context, _ *srvContractPb.ReportMetadataRequest) (*srvContractPb.ReportMetadataReply, error) {\n\treturn &srvContractPb.ReportMetadataReply{}, nil\n}\n\ntype testAppInfo struct {\n\tid       string\n\tname     string\n\tversion  string\n\tmetaData map[string]string\n\tendpoint []string\n}\n\nfunc (t testAppInfo) ID() string {\n\treturn t.id\n}\n\nfunc (t testAppInfo) Name() string {\n\treturn t.name\n}\n\nfunc (t testAppInfo) Version() string {\n\treturn t.version\n}\n\nfunc (t testAppInfo) Metadata() map[string]string {\n\treturn t.metaData\n}\n\nfunc (t testAppInfo) Endpoint() []string {\n\treturn t.endpoint\n}\n\nfunc TestWithEndpoint(t *testing.T) {\n\to := &options{}\n\tv := \"127.0.0.1:9090\"\n\tWithEndpoint(v)(o)\n\tif !reflect.DeepEqual(v, o.Endpoint) {\n\t\tt.Fatalf(\"o.Endpoint:%s is not equal to v:%s\", o.Endpoint, v)\n\t}\n}\n\nfunc TestOptionsParseJSON(t *testing.T) {\n\twant := &options{\n\t\tEndpoint: \"127.0.0.1:9090\",\n\t}\n\to := &options{}\n\tif err := o.ParseJSON([]byte(`{\"endpoint\":\"127.0.0.1:9090\"}`)); err != nil {\n\t\tt.Fatalf(\"o.ParseJSON(v) error:%s\", err)\n\t}\n\tif !reflect.DeepEqual(o, want) {\n\t\tt.Fatalf(\"o:%v is not equal to want:%v\", o, want)\n\t}\n}\n\nfunc TestListDescriptors(t *testing.T) {\n\ttestPb := &descriptorpb.FileDescriptorProto{\n\t\tSyntax:  proto.String(\"proto3\"),\n\t\tName:    proto.String(\"test.proto\"),\n\t\tPackage: proto.String(\"test\"),\n\t\tMessageType: []*descriptorpb.DescriptorProto{\n\t\t\t{\n\t\t\t\tName: proto.String(\"TestMessage\"),\n\t\t\t\tField: []*descriptorpb.FieldDescriptorProto{\n\t\t\t\t\t{\n\t\t\t\t\t\tName:     proto.String(\"id\"),\n\t\t\t\t\t\tJsonName: proto.String(\"id\"),\n\t\t\t\t\t\tNumber:   proto.Int32(1),\n\t\t\t\t\t\tType:     descriptorpb.FieldDescriptorProto_Type(pref.Int32Kind).Enum(),\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tName:     proto.String(\"name\"),\n\t\t\t\t\t\tJsonName: proto.String(\"name\"),\n\t\t\t\t\t\tNumber:   proto.Int32(2),\n\t\t\t\t\t\tType:     descriptorpb.FieldDescriptorProto_Type(pref.StringKind).Enum(),\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\tService: []*descriptorpb.ServiceDescriptorProto{\n\t\t\t{\n\t\t\t\tName: proto.String(\"TestService\"),\n\t\t\t\tMethod: []*descriptorpb.MethodDescriptorProto{\n\t\t\t\t\t{\n\t\t\t\t\t\tName:       proto.String(\"Create\"),\n\t\t\t\t\t\tInputType:  proto.String(\"TestMessage\"),\n\t\t\t\t\t\tOutputType: proto.String(\"TestMessage\"),\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t}\n\n\tfd, err := protodesc.NewFile(testPb, nil)\n\tif err != nil {\n\t\tt.Fatalf(\"protodesc.NewFile(pb, nil) error:%s\", err)\n\t}\n\n\tprotoregistry.GlobalFiles = new(protoregistry.Files)\n\terr = protoregistry.GlobalFiles.RegisterFile(fd)\n\tif err != nil {\n\t\tt.Fatalf(\"protoregistry.GlobalFiles.RegisterFile(fd) error:%s\", err)\n\t}\n\n\twant := struct {\n\t\tservices []*srvContractPb.ServiceDescriptor\n\t\ttypes    []*srvContractPb.TypeDescriptor\n\t}{\n\t\tservices: []*srvContractPb.ServiceDescriptor{\n\t\t\t{\n\t\t\t\tName: \"TestService\",\n\t\t\t\tMethods: []*srvContractPb.MethodDescriptor{\n\t\t\t\t\t{\n\t\t\t\t\t\tName:            \"Create\",\n\t\t\t\t\t\tInputTypes:      []string{\"test.TestMessage\"},\n\t\t\t\t\t\tOutputTypes:     []string{\"test.TestMessage\"},\n\t\t\t\t\t\tClientStreaming: proto.Bool(false),\n\t\t\t\t\t\tServerStreaming: proto.Bool(false),\n\t\t\t\t\t\tDescription:     nil,\n\t\t\t\t\t\tHttpPaths:       []string{\"\"},\n\t\t\t\t\t\tHttpMethods:     []string{\"\"},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\ttypes: []*srvContractPb.TypeDescriptor{\n\t\t\t{\n\t\t\t\tName: \"TestMessage\",\n\t\t\t\tFields: []*srvContractPb.FieldDescriptor{\n\t\t\t\t\t{\n\t\t\t\t\t\tName:     \"id\",\n\t\t\t\t\t\tNumber:   int32(1),\n\t\t\t\t\t\tType:     srvContractPb.FieldDescriptor_TYPE_INT32,\n\t\t\t\t\t\tTypeName: proto.String(\"int32\"),\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tName:     \"name\",\n\t\t\t\t\t\tNumber:   int32(2),\n\t\t\t\t\t\tType:     srvContractPb.FieldDescriptor_TYPE_STRING,\n\t\t\t\t\t\tTypeName: proto.String(\"string\"),\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t}\n\n\tservices, types, err := listDescriptors()\n\tif err != nil {\n\t\tt.Fatalf(\"listDescriptors error:%s\", err)\n\t}\n\n\tif !reflect.DeepEqual(services, want.services) {\n\t\tt.Fatalf(\"services:%v is not equal to want.services:%v\", services, want.services)\n\t}\n\tif !reflect.DeepEqual(types, want.types) {\n\t\tt.Fatalf(\"types:%v is not equal to want.types:%v\", types, want.types)\n\t}\n}\n\nfunc TestHTTPPatternInfo(t *testing.T) {\n\ttype args struct {\n\t\tpattern any\n\t}\n\ttests := []struct {\n\t\tname       string\n\t\targs       args\n\t\twantMethod string\n\t\twantPath   string\n\t}{\n\t\t{\n\t\t\tname: \"get\",\n\t\t\targs: args{\n\t\t\t\tpattern: &annotations.HttpRule_Get{Get: \"/foo\"},\n\t\t\t},\n\t\t\twantMethod: http.MethodGet,\n\t\t\twantPath:   \"/foo\",\n\t\t},\n\t\t{\n\t\t\tname: \"post\",\n\t\t\targs: args{\n\t\t\t\tpattern: &annotations.HttpRule_Post{Post: \"/foo\"},\n\t\t\t},\n\t\t\twantMethod: http.MethodPost,\n\t\t\twantPath:   \"/foo\",\n\t\t},\n\t\t{\n\t\t\tname: \"put\",\n\t\t\targs: args{\n\t\t\t\tpattern: &annotations.HttpRule_Put{Put: \"/foo\"},\n\t\t\t},\n\t\t\twantMethod: http.MethodPut,\n\t\t\twantPath:   \"/foo\",\n\t\t},\n\t\t{\n\t\t\tname: \"delete\",\n\t\t\targs: args{\n\t\t\t\tpattern: &annotations.HttpRule_Delete{Delete: \"/foo\"},\n\t\t\t},\n\t\t\twantMethod: http.MethodDelete,\n\t\t\twantPath:   \"/foo\",\n\t\t},\n\t\t{\n\t\t\tname: \"patch\",\n\t\t\targs: args{\n\t\t\t\tpattern: &annotations.HttpRule_Patch{Patch: \"/foo\"},\n\t\t\t},\n\t\t\twantMethod: http.MethodPatch,\n\t\t\twantPath:   \"/foo\",\n\t\t},\n\t\t{\n\t\t\tname: \"custom\",\n\t\t\targs: args{\n\t\t\t\tpattern: &annotations.HttpRule_Custom{\n\t\t\t\t\tCustom: &annotations.CustomHttpPattern{\n\t\t\t\t\t\tKind: \"CUSTOM\",\n\t\t\t\t\t\tPath: \"/foo\",\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\twantMethod: \"CUSTOM\",\n\t\t\twantPath:   \"/foo\",\n\t\t},\n\t\t{\n\t\t\tname: \"other\",\n\t\t\targs: args{\n\t\t\t\tpattern: nil,\n\t\t\t},\n\t\t\twantMethod: \"\",\n\t\t\twantPath:   \"\",\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tgotMethod, gotPath := HTTPPatternInfo(tt.args.pattern)\n\t\t\tif gotMethod != tt.wantMethod {\n\t\t\t\tt.Errorf(\"HTTPPatternInfo() gotMethod = %v, want %v\", gotMethod, tt.wantMethod)\n\t\t\t}\n\t\t\tif gotPath != tt.wantPath {\n\t\t\t\tt.Errorf(\"HTTPPatternInfo() gotPath = %v, want %v\", gotPath, tt.wantPath)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestOpenSergo(t *testing.T) {\n\tsrv := grpc.NewServer()\n\tsrvContractPb.RegisterMetadataServiceServer(srv, new(testMetadataServiceServer))\n\tlis, err := net.Listen(\"tcp\", \"127.0.0.1:9090\")\n\tif err != nil {\n\t\tt.Fatalf(\"net.Listen error:%s\", err)\n\t}\n\tgo func() {\n\t\terr := srv.Serve(lis)\n\t\tif err != nil {\n\t\t\tpanic(err)\n\t\t}\n\t}()\n\n\tapp := &testAppInfo{\n\t\tname:     \"testApp\",\n\t\tendpoint: []string{\"//example.com:9090\", \"//foo.com:9090\"},\n\t}\n\n\ttype args struct {\n\t\topts []Option\n\t}\n\ttests := []struct {\n\t\tname      string\n\t\targs      args\n\t\tpreFunc   func(t *testing.T)\n\t\tdeferFunc func(t *testing.T)\n\t\twantErr   bool\n\t}{\n\t\t{\n\t\t\tname: \"test_with_opts\",\n\t\t\targs: args{\n\t\t\t\topts: []Option{\n\t\t\t\t\tWithEndpoint(\"127.0.0.1:9090\"),\n\t\t\t\t},\n\t\t\t},\n\t\t\twantErr: false,\n\t\t},\n\t\t{\n\t\t\tname: \"test_with_env_endpoint\",\n\t\t\targs: args{\n\t\t\t\topts: []Option{},\n\t\t\t},\n\t\t\tpreFunc: func(_ *testing.T) {\n\t\t\t\terr := os.Setenv(\"OPENSERGO_ENDPOINT\", \"127.0.0.1:9090\")\n\t\t\t\tif err != nil {\n\t\t\t\t\tpanic(err)\n\t\t\t\t}\n\t\t\t},\n\t\t\twantErr: false,\n\t\t},\n\t\t{\n\t\t\tname: \"test_with_env_config_file\",\n\t\t\targs: args{\n\t\t\t\topts: []Option{},\n\t\t\t},\n\t\t\tpreFunc: func(_ *testing.T) {\n\t\t\t\terr := os.Setenv(\"OPENSERGO_BOOTSTRAP\", `{\"endpoint\": \"127.0.0.1:9090\"}`)\n\t\t\t\tif err != nil {\n\t\t\t\t\tpanic(err)\n\t\t\t\t}\n\t\t\t},\n\t\t\twantErr: false,\n\t\t},\n\t\t{\n\t\t\tname: \"test_with_env_bootstrap\",\n\t\t\targs: args{\n\t\t\t\topts: []Option{},\n\t\t\t},\n\t\t\tpreFunc: func(t *testing.T) {\n\t\t\t\tfileContent := `{\"endpoint\": \"127.0.0.1:9090\"}`\n\t\t\t\terr := os.WriteFile(\"test.json\", []byte(fileContent), 0o644)\n\t\t\t\tif err != nil {\n\t\t\t\t\tt.Fatalf(\"os.WriteFile error:%s\", err)\n\t\t\t\t}\n\t\t\t\tconfPath, err := filepath.Abs(\"./test.json\")\n\t\t\t\tif err != nil {\n\t\t\t\t\tt.Fatalf(\"filepath.Abs error:%s\", err)\n\t\t\t\t}\n\t\t\t\terr = os.Setenv(\"OPENSERGO_BOOTSTRAP_CONFIG\", confPath)\n\t\t\t\tif err != nil {\n\t\t\t\t\tpanic(err)\n\t\t\t\t}\n\t\t\t},\n\t\t\tdeferFunc: func(t *testing.T) {\n\t\t\t\tpath := os.Getenv(\"OPENSERGO_BOOTSTRAP_CONFIG\")\n\t\t\t\tif path != \"\" {\n\t\t\t\t\terr := os.Remove(path)\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\tt.Fatalf(\"os.Remove error:%s\", err)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t},\n\t\t\twantErr: false,\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tif tt.preFunc != nil {\n\t\t\t\ttt.preFunc(t)\n\t\t\t}\n\t\t\tif tt.deferFunc != nil {\n\t\t\t\tdefer tt.deferFunc(t)\n\t\t\t}\n\t\t\tosServer, err := New(tt.args.opts...)\n\t\t\tif (err != nil) != tt.wantErr {\n\t\t\t\tt.Errorf(\"New() error = %v, wantErr %v\", err, tt.wantErr)\n\t\t\t\treturn\n\t\t\t}\n\t\t\terr = osServer.ReportMetadata(context.Background(), app)\n\t\t\tif (err != nil) != tt.wantErr {\n\t\t\t\tt.Errorf(\"ReportMetadata() error = %v, wantErr %v\", err, tt.wantErr)\n\t\t\t\treturn\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "contrib/polaris/config.go",
    "content": "package polaris\n\nimport (\n\t\"context\"\n\t\"path/filepath\"\n\t\"strings\"\n\n\t\"github.com/polarismesh/polaris-go\"\n\t\"github.com/polarismesh/polaris-go/pkg/model\"\n\n\t\"github.com/go-kratos/kratos/v2/config\"\n\t\"github.com/go-kratos/kratos/v2/log\"\n)\n\n// ConfigOption is polaris config option.\ntype ConfigOption func(o *configOptions)\n\ntype configOptions struct {\n\tnamespace  string\n\tfiles      []File\n\tconfigFile []polaris.ConfigFile\n}\n\n// WithConfigFile with polaris config file\nfunc WithConfigFile(file ...File) ConfigOption {\n\treturn func(o *configOptions) {\n\t\to.files = file\n\t}\n}\n\ntype File struct {\n\tName  string\n\tGroup string\n}\n\ntype source struct {\n\tclient  polaris.ConfigAPI\n\toptions *configOptions\n}\n\n// Load return the config values\nfunc (s *source) Load() ([]*config.KeyValue, error) {\n\tkvs := make([]*config.KeyValue, 0, len(s.options.files))\n\tfor _, file := range s.options.files {\n\t\tconfigFile, err := s.client.GetConfigFile(s.options.namespace, file.Group, file.Name)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\ts.options.configFile = append(s.options.configFile, configFile)\n\t\tkvs = append(kvs, &config.KeyValue{\n\t\t\tKey:    file.Name,\n\t\t\tValue:  []byte(configFile.GetContent()),\n\t\t\tFormat: strings.TrimPrefix(filepath.Ext(file.Name), \".\"),\n\t\t})\n\t}\n\treturn kvs, nil\n}\n\n// Watch return the watcher\nfunc (s *source) Watch() (config.Watcher, error) {\n\treturn newConfigWatcher(s.options.configFile), nil\n}\n\ntype ConfigWatcher struct {\n\tevent chan model.ConfigFileChangeEvent\n\tcfg   []*config.KeyValue\n}\n\nfunc receive(event chan model.ConfigFileChangeEvent) func(m model.ConfigFileChangeEvent) {\n\treturn func(m model.ConfigFileChangeEvent) {\n\t\tdefer func() {\n\t\t\tif err := recover(); err != nil {\n\t\t\t\tlog.Error(err)\n\t\t\t}\n\t\t}()\n\t\tevent <- m\n\t}\n}\n\nfunc newConfigWatcher(configFile []polaris.ConfigFile) *ConfigWatcher {\n\tw := &ConfigWatcher{\n\t\tevent: make(chan model.ConfigFileChangeEvent, len(configFile)),\n\t}\n\tfor _, file := range configFile {\n\t\tw.cfg = append(w.cfg, &config.KeyValue{\n\t\t\tKey:    file.GetFileName(),\n\t\t\tValue:  []byte(file.GetContent()),\n\t\t\tFormat: strings.TrimPrefix(filepath.Ext(file.GetFileName()), \".\"),\n\t\t})\n\t}\n\tfor _, file := range configFile {\n\t\tfile.AddChangeListener(receive(w.event))\n\t}\n\treturn w\n}\n\nfunc (w *ConfigWatcher) Next() ([]*config.KeyValue, error) {\n\tif event, ok := <-w.event; ok {\n\t\tm := make(map[string]*config.KeyValue)\n\t\tfor _, file := range w.cfg {\n\t\t\tm[file.Key] = file\n\t\t}\n\t\tm[event.ConfigFileMetadata.GetFileName()] = &config.KeyValue{\n\t\t\tKey:    event.ConfigFileMetadata.GetFileName(),\n\t\t\tValue:  []byte(event.NewValue),\n\t\t\tFormat: strings.TrimPrefix(filepath.Ext(event.ConfigFileMetadata.GetFileName()), \".\"),\n\t\t}\n\t\tw.cfg = make([]*config.KeyValue, 0, len(m))\n\t\tfor _, kv := range m {\n\t\t\tw.cfg = append(w.cfg, kv)\n\t\t}\n\t\treturn w.cfg, nil\n\t}\n\treturn nil, context.Canceled\n}\n\nfunc (w *ConfigWatcher) Stop() error {\n\tclose(w.event)\n\treturn nil\n}\n"
  },
  {
    "path": "contrib/polaris/config_test.go",
    "content": "package polaris\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"reflect\"\n\t\"strings\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/go-kratos/kratos/v2/config\"\n\n\t\"github.com/polarismesh/polaris-go\"\n)\n\nvar (\n\ttestNamespace     = \"default\"\n\ttestFileGroup     = \"test\"\n\ttestOriginContent = `server:\n\t\tport: 8080`\n\ttestUpdatedContent = `server:\n\t\tport: 8090`\n\ttestCenterURL = \"http://127.0.0.1:8090\"\n)\n\nfunc makeJSONRequest(uri string, data string, method string, headers map[string]string) ([]byte, error) {\n\tclient := http.Client{}\n\treq, err := http.NewRequest(method, uri, strings.NewReader(data))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treq.Header.Add(\"Content-Type\", \"application/json\")\n\tfor k, v := range headers {\n\t\treq.Header.Add(k, v)\n\t}\n\tres, err := client.Do(req)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdefer res.Body.Close()\n\treturn io.ReadAll(res.Body)\n}\n\ntype commonRes struct {\n\tCode int32 `json:\"code\"`\n}\n\ntype LoginRes struct {\n\tCode          int32 `json:\"code\"`\n\tLoginResponse struct {\n\t\tToken string `json:\"token\"`\n\t} `json:\"loginResponse\"`\n}\n\ntype configClient struct {\n\ttoken string\n}\n\nfunc newConfigClient() (*configClient, error) {\n\ttoken, err := getToken(testCenterURL)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn &configClient{\n\t\ttoken: token,\n\t}, nil\n}\n\nfunc getToken(testCenterURL string) (string, error) {\n\tdata, err := json.Marshal(map[string]string{\n\t\t\"name\":     \"polaris\",\n\t\t\"password\": \"polaris\",\n\t})\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\t// login use default user\n\tres, err := makeJSONRequest(fmt.Sprintf(\"%s/core/v1/user/login\", testCenterURL), string(data), http.MethodPost, map[string]string{})\n\tif err != nil {\n\t\treturn \"\", nil\n\t}\n\tvar loginRes LoginRes\n\tif err = json.Unmarshal(res, &loginRes); err != nil {\n\t\treturn \"\", err\n\t}\n\treturn loginRes.LoginResponse.Token, nil\n}\n\nfunc (client *configClient) createConfigFile(name string) error {\n\tdata, err := json.Marshal(map[string]string{\n\t\t\"name\":      name,\n\t\t\"namespace\": testNamespace,\n\t\t\"group\":     testFileGroup,\n\t\t\"content\":   testOriginContent,\n\t\t\"modifyBy\":  \"polaris\",\n\t\t\"format\":    \"yaml\",\n\t})\n\tif err != nil {\n\t\treturn err\n\t}\n\tres, err := makeJSONRequest(fmt.Sprintf(\"%s/config/v1/configfiles\", testCenterURL), string(data), http.MethodPost, map[string]string{\n\t\t\"X-Polaris-Token\": client.token,\n\t})\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tvar resJSON commonRes\n\terr = json.Unmarshal(res, &resJSON)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif resJSON.Code != 200000 {\n\t\treturn fmt.Errorf(\"create error, res: %s\", string(res))\n\t}\n\treturn nil\n}\n\nfunc (client *configClient) updateConfigFile(name string) error {\n\tdata, err := json.Marshal(map[string]string{\n\t\t\"name\":      name,\n\t\t\"namespace\": testNamespace,\n\t\t\"group\":     testFileGroup,\n\t\t\"content\":   testUpdatedContent,\n\t\t\"modifyBy\":  \"polaris\",\n\t\t\"format\":    \"yaml\",\n\t})\n\tif err != nil {\n\t\treturn err\n\t}\n\tres, err := makeJSONRequest(fmt.Sprintf(\"%s/config/v1/configfiles\", testCenterURL), string(data), http.MethodPut, map[string]string{\n\t\t\"X-Polaris-Token\": client.token,\n\t})\n\tif err != nil {\n\t\treturn err\n\t}\n\tvar resJSON commonRes\n\terr = json.Unmarshal(res, &resJSON)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif resJSON.Code != 200000 {\n\t\treturn fmt.Errorf(\"update error, res: %s\", string(res))\n\t}\n\treturn nil\n}\n\nfunc (client *configClient) deleteConfigFile(name string) error {\n\tdata, err := json.Marshal(map[string]string{})\n\tif err != nil {\n\t\treturn err\n\t}\n\turl := fmt.Sprintf(\"%s/config/v1/configfiles?namespace=%s&group=%s&name=%s\", testCenterURL, testNamespace, testFileGroup, name)\n\tres, err := makeJSONRequest(url, string(data), http.MethodDelete, map[string]string{\n\t\t\"X-Polaris-Token\": client.token,\n\t})\n\tif err != nil {\n\t\treturn err\n\t}\n\tvar resJSON commonRes\n\terr = json.Unmarshal(res, &resJSON)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif resJSON.Code != 200000 {\n\t\treturn fmt.Errorf(\"delete error, res: %s\", string(res))\n\t}\n\treturn nil\n}\n\nfunc (client *configClient) publishConfigFile(name string) error {\n\tdata, err := json.Marshal(map[string]string{\n\t\t\"namespace\": testNamespace,\n\t\t\"group\":     testFileGroup,\n\t\t\"fileName\":  name,\n\t\t\"name\":      name,\n\t})\n\tif err != nil {\n\t\treturn err\n\t}\n\tres, err := makeJSONRequest(fmt.Sprintf(\"%s/config/v1/configfiles/release\", testCenterURL), string(data), http.MethodPost, map[string]string{\n\t\t\"X-Polaris-Token\": client.token,\n\t})\n\tif err != nil {\n\t\treturn err\n\t}\n\tvar resJSON commonRes\n\terr = json.Unmarshal(res, &resJSON)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif resJSON.Code != 200000 {\n\t\treturn fmt.Errorf(\"publish error, res: %s\", string(res))\n\t}\n\treturn nil\n}\n\nfunc TestConfig(t *testing.T) {\n\tname := \"kratos-polaris-test.yaml\"\n\tclient, err := newConfigClient()\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\t_ = client.deleteConfigFile(name)\n\tif err = client.createConfigFile(name); err != nil {\n\t\tt.Fatal(err)\n\t}\n\ttime.Sleep(5 * time.Second)\n\tif err = client.publishConfigFile(name); err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\ttime.Sleep(5 * time.Second)\n\n\t// Always remember clear test resource\n\tsdk, err := polaris.NewSDKContextByAddress(\"127.0.0.1:8091\")\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tp := New(sdk)\n\tconfig, err := p.Config(WithConfigFile(File{Name: name, Group: testFileGroup}))\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tkv, err := config.Load()\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tfor _, value := range kv {\n\t\tt.Logf(\"key: %s, value: %s\", value.Key, value.Value)\n\t}\n\tif len(kv) != 1 || kv[0].Key != name || string(kv[0].Value) != testOriginContent {\n\t\tt.Fatal(\"config error\")\n\t}\n\n\tw, err := config.Watch()\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tt.Cleanup(func() {\n\t\terr = client.deleteConfigFile(name)\n\t\tif err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\t})\n\n\tif err = client.updateConfigFile(name); err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tif err = client.publishConfigFile(name); err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tif kv, err = w.Next(); err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tfor _, value := range kv {\n\t\tt.Log(value.Key, string(value.Value))\n\t}\n\n\tif len(kv) != 1 || kv[0].Key != name || string(kv[0].Value) != testUpdatedContent {\n\t\tt.Fatal(\"config error\")\n\t}\n}\n\nfunc TestExtToFormat(t *testing.T) {\n\tname := \"kratos-polaris-ext.yaml\"\n\tclient, err := newConfigClient()\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\t_ = client.deleteConfigFile(name)\n\tif err = client.createConfigFile(name); err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif err = client.publishConfigFile(name); err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\t// Always remember clear test resource\n\tt.Cleanup(func() {\n\t\tif err = client.deleteConfigFile(name); err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\t})\n\n\tsdk, err := polaris.NewSDKContextByAddress(\"127.0.0.1:8091\")\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tp := New(sdk)\n\n\tcfg, err := p.Config(WithConfigFile(File{Name: name, Group: testFileGroup}))\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tkv, err := cfg.Load()\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif !reflect.DeepEqual(len(kv), 1) {\n\t\tt.Errorf(\"len(kvs) = %d\", len(kv))\n\t}\n\tif !reflect.DeepEqual(name, kv[0].Key) {\n\t\tt.Errorf(\"kvs[0].Key is %s\", kv[0].Key)\n\t}\n\tif !reflect.DeepEqual(testOriginContent, string(kv[0].Value)) {\n\t\tt.Errorf(\"kvs[0].Value is %s\", kv[0].Value)\n\t}\n\tif !reflect.DeepEqual(\"yaml\", kv[0].Format) {\n\t\tt.Errorf(\"kvs[0].Format is %s\", kv[0].Format)\n\t}\n}\n\nfunc TestGetMultipleConfig(t *testing.T) {\n\tclient, err := newConfigClient()\n\tfiles := make([]File, 0, 3)\n\tfor i := 0; i < 3; i++ {\n\t\tname := fmt.Sprintf(\"kratos-polaris-test-%d.yaml\", i)\n\t\tif err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\t\t_ = client.deleteConfigFile(name)\n\t\tif err = client.createConfigFile(name); err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\t\tif err = client.publishConfigFile(name); err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\t\tfiles = append(files, File{Name: name, Group: testFileGroup})\n\t}\n\n\tsdk, err := polaris.NewSDKContextByAddress(\"127.0.0.1:8091\")\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tp := New(sdk, WithNamespace(\"default\"))\n\n\tcfg, err := p.Config(WithConfigFile(files...))\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tkvs, err := cfg.Load()\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tfor _, kv := range kvs {\n\t\tt.Logf(\"key: %s, value: %s\", kv.Key, kv.Value)\n\t}\n\n\tw, err := cfg.Watch()\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tfor _, file := range files {\n\t\tif err = client.publishConfigFile(file.Name); err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\t\tkvs, err := w.Next()\n\t\tif err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\t\tm := make(map[string]*config.KeyValue)\n\t\tfor _, kv := range kvs {\n\t\t\tm[kv.Key] = kv\n\t\t}\n\t\tif !reflect.DeepEqual(file.Name, m[file.Name].Key) {\n\t\t\tt.Errorf(\"m[file.Name].Key is %s\", m[file.Name].Key)\n\t\t}\n\t\tif !reflect.DeepEqual(testOriginContent, string(m[file.Name].Value)) {\n\t\t\tt.Errorf(\"m[file.Name].Value is %s\", m[file.Name].Value)\n\t\t}\n\t\tif !reflect.DeepEqual(\"yaml\", m[file.Name].Format) {\n\t\t\tt.Errorf(\"m[file.Name].Format is %s\", m[file.Name].Format)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "contrib/polaris/go.mod",
    "content": "module github.com/go-kratos/kratos/contrib/polaris/v2\n\ngo 1.23.0\n\ntoolchain go1.24.6\n\nrequire (\n\tgithub.com/go-kratos/aegis v0.2.0\n\tgithub.com/go-kratos/kratos/v2 v2.9.2\n\tgithub.com/google/uuid v1.4.0\n\tgithub.com/polarismesh/polaris-go v1.3.0\n\tgoogle.golang.org/protobuf v1.33.0\n)\n\nrequire (\n\tdario.cat/mergo v1.0.0 // indirect\n\tgithub.com/beorn7/perks v1.0.1 // indirect\n\tgithub.com/cespare/xxhash/v2 v2.2.0 // indirect\n\tgithub.com/dlclark/regexp2 v1.7.0 // indirect\n\tgithub.com/go-playground/form/v4 v4.2.0 // indirect\n\tgithub.com/golang/protobuf v1.5.4 // indirect\n\tgithub.com/gorilla/mux v1.8.1 // indirect\n\tgithub.com/hashicorp/errwrap v1.0.0 // indirect\n\tgithub.com/hashicorp/go-multierror v1.1.1 // indirect\n\tgithub.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect\n\tgithub.com/mitchellh/go-homedir v1.1.0 // indirect\n\tgithub.com/modern-go/reflect2 v1.0.2 // indirect\n\tgithub.com/natefinch/lumberjack v2.0.0+incompatible // indirect\n\tgithub.com/prometheus/client_golang v1.12.1 // indirect\n\tgithub.com/prometheus/client_model v0.2.0 // indirect\n\tgithub.com/prometheus/common v0.32.1 // indirect\n\tgithub.com/prometheus/procfs v0.7.3 // indirect\n\tgithub.com/spaolacci/murmur3 v1.1.0 // indirect\n\tgo.uber.org/atomic v1.7.0 // indirect\n\tgo.uber.org/multierr v1.6.0 // indirect\n\tgo.uber.org/zap v1.21.0 // indirect\n\tgolang.org/x/net v0.39.0 // indirect\n\tgolang.org/x/sync v0.13.0 // indirect\n\tgolang.org/x/sys v0.32.0 // indirect\n\tgolang.org/x/text v0.24.0 // indirect\n\tgoogle.golang.org/genproto/googleapis/rpc v0.0.0-20240102182953-50ed04b92917 // indirect\n\tgoogle.golang.org/grpc v1.61.1 // indirect\n\tgopkg.in/yaml.v2 v2.4.0 // indirect\n\tgopkg.in/yaml.v3 v3.0.1 // indirect\n)\n\nreplace github.com/go-kratos/kratos/v2 => ../../\n"
  },
  {
    "path": "contrib/polaris/go.sum",
    "content": "cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=\ncloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=\ncloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=\ncloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=\ncloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=\ncloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=\ncloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=\ncloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To=\ncloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4=\ncloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M=\ncloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc=\ncloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk=\ncloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs=\ncloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc=\ncloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY=\ncloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=\ncloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=\ncloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=\ncloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=\ncloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=\ncloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=\ncloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=\ncloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=\ncloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=\ncloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=\ncloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=\ncloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU=\ncloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=\ncloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=\ncloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=\ncloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=\ncloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=\ndario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk=\ndario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=\ndmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=\ngithub.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=\ngithub.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=\ngithub.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=\ngithub.com/agiledragon/gomonkey v2.0.2+incompatible h1:eXKi9/piiC3cjJD1658mEE2o3NjkJ5vDLgYjCQu0Xlw=\ngithub.com/agiledragon/gomonkey v2.0.2+incompatible/go.mod h1:2NGfXu1a80LLr2cmWXGBDaHEjb1idR6+FVlX5T3D9hw=\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/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=\ngithub.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=\ngithub.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=\ngithub.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=\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 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=\ngithub.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=\ngithub.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=\ngithub.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=\ngithub.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=\ngithub.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=\ngithub.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=\ngithub.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=\ngithub.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=\ngithub.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=\ngithub.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=\ngithub.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=\ngithub.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=\ngithub.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI=\ngithub.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=\ngithub.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=\ngithub.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=\ngithub.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.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/dlclark/regexp2 v1.7.0 h1:7lJfhqlPssTb1WQx4yvTHN0uElPEv52sbaECrAQxjAo=\ngithub.com/dlclark/regexp2 v1.7.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=\ngithub.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=\ngithub.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=\ngithub.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=\ngithub.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=\ngithub.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE=\ngithub.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=\ngithub.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=\ngithub.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=\ngithub.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=\ngithub.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=\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-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=\ngithub.com/go-kratos/aegis v0.2.0 h1:dObzCDWn3XVjUkgxyBp6ZeWtx/do0DPZ7LY3yNSJLUQ=\ngithub.com/go-kratos/aegis v0.2.0/go.mod h1:v0R2m73WgEEYB3XYu6aE2WcMwsZkJ/Rzuf5eVccm7bI=\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-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=\ngithub.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A=\ngithub.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=\ngithub.com/go-playground/form/v4 v4.2.0 h1:N1wh+Goz61e6w66vo8vJkQt+uwZSoLz50kZPJWR8eic=\ngithub.com/go-playground/form/v4 v4.2.0/go.mod h1:q1a2BY+AQUUzhl6xA/6hBetay6dEIhMHjgvJiGo6K7U=\ngithub.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=\ngithub.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=\ngithub.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=\ngithub.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=\ngithub.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=\ngithub.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=\ngithub.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=\ngithub.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=\ngithub.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=\ngithub.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=\ngithub.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=\ngithub.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=\ngithub.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=\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.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=\ngithub.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=\ngithub.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=\ngithub.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=\ngithub.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=\ngithub.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=\ngithub.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=\ngithub.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=\ngithub.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=\ngithub.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=\ngithub.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=\ngithub.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=\ngithub.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=\ngithub.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=\ngithub.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=\ngithub.com/gonum/blas v0.0.0-20181208220705-f22b278b28ac/go.mod h1:P32wAyui1PQ58Oce/KYkOqQv8cVw1zAapXOl+dRFGbc=\ngithub.com/gonum/floats v0.0.0-20181209220543-c233463c7e82/go.mod h1:PxC8OnwL11+aosOB5+iEPoV3picfs8tUpkVd0pDo+Kg=\ngithub.com/gonum/integrate v0.0.0-20181209220457-a422b5c0fdf2/go.mod h1:pDgmNM6seYpwvPos3q+zxlXMsbve6mOIPucUnUOrI7Y=\ngithub.com/gonum/internal v0.0.0-20181124074243-f884aa714029/go.mod h1:Pu4dmpkhSyOzRwuXkOgAvijx4o+4YMUJJo9OvPYMkks=\ngithub.com/gonum/lapack v0.0.0-20181123203213-e4cdc5a0bff9/go.mod h1:XA3DeT6rxh2EAE789SSiSJNqxPaC0aE9J8NTOI0Jo/A=\ngithub.com/gonum/matrix v0.0.0-20181209220409-c518dec07be9/go.mod h1:0EXg4mc1CNP0HCqCz+K4ts155PXIlUywf0wqN+GfPZw=\ngithub.com/gonum/stat v0.0.0-20181125101827-41a0da705a5b/go.mod h1:Z4GIJBJO3Wa4gD4vbwQxXXZ+WHmW6E9ixmNrwvs0iZs=\ngithub.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=\ngithub.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=\ngithub.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=\ngithub.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=\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.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\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/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=\ngithub.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=\ngithub.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=\ngithub.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=\ngithub.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=\ngithub.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=\ngithub.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=\ngithub.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=\ngithub.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=\ngithub.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=\ngithub.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=\ngithub.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=\ngithub.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=\ngithub.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4=\ngithub.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=\ngithub.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=\ngithub.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=\ngithub.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=\ngithub.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=\ngithub.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=\ngithub.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=\ngithub.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=\ngithub.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA=\ngithub.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=\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/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=\ngithub.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=\ngithub.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=\ngithub.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=\ngithub.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=\ngithub.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=\ngithub.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=\ngithub.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=\ngithub.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=\ngithub.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=\ngithub.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=\ngithub.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=\ngithub.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=\ngithub.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=\ngithub.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=\ngithub.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=\ngithub.com/konsorten/go-windows-terminal-sequences v1.0.3/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 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=\ngithub.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=\ngithub.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=\ngithub.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=\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/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=\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/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=\ngithub.com/natefinch/lumberjack v2.0.0+incompatible h1:4QJd3OLAMgj7ph+yZTuX13Ld4UpgHp07nNdFX7mqFfM=\ngithub.com/natefinch/lumberjack v2.0.0+incompatible/go.mod h1:Wi9p2TTF5DG5oU+6YfsmYQpsTIOm0B1VNzQg9Mw6nPk=\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 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=\ngithub.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=\ngithub.com/polarismesh/polaris-go v1.3.0 h1:KZKX//ow4OPPoS5+s7h07ptprg+2AcNVGrN6WakC9QM=\ngithub.com/polarismesh/polaris-go v1.3.0/go.mod h1:HsN0ierETIujHpmnnYJ3qkwQw4QGAECuHvBZTDaw1tI=\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.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=\ngithub.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0=\ngithub.com/prometheus/client_golang v1.12.1 h1:ZiaPsmm9uiBeaSMRznKsCDNtPCS0T3JVDGF+06gjBzk=\ngithub.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY=\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.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=\ngithub.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M=\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.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=\ngithub.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc=\ngithub.com/prometheus/common v0.32.1 h1:hWIdL3N2HoUx3B8j3YN9mWor0qhY/NlEKZEaXxuIRh4=\ngithub.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls=\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.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=\ngithub.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=\ngithub.com/prometheus/procfs v0.7.3 h1:4jVXhlkAyzOScmCkXBTOLRLTz8EeU+eyjrwB/EPq0VU=\ngithub.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=\ngithub.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=\ngithub.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=\ngithub.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=\ngithub.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=\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/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=\ngithub.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=\ngithub.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=\ngithub.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=\ngithub.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=\ngithub.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI=\ngithub.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=\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/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.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=\ngithub.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngithub.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=\ngithub.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=\ngithub.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=\ngithub.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=\ngithub.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=\ngithub.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=\ngo.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=\ngo.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=\ngo.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=\ngo.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=\ngo.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=\ngo.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=\ngo.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw=\ngo.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=\ngo.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI=\ngo.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=\ngo.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4=\ngo.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=\ngo.uber.org/zap v1.21.0 h1:WefMeulhovoZ2sYXz7st6K0sLj7bBhpiFaud4r4zST8=\ngo.uber.org/zap v1.21.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw=\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-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\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/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=\ngolang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=\ngolang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=\ngolang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=\ngolang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=\ngolang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=\ngolang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=\ngolang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=\ngolang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=\ngolang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=\ngolang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=\ngolang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=\ngolang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=\ngolang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=\ngolang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=\ngolang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=\ngolang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=\ngolang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=\ngolang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=\ngolang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=\ngolang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=\ngolang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=\ngolang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=\ngolang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=\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/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=\ngolang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=\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-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20200222125558-5a598a2470a0/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-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=\ngolang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=\ngolang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=\ngolang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=\ngolang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=\ngolang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=\ngolang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=\ngolang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=\ngolang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=\ngolang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=\ngolang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=\ngolang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=\ngolang.org/x/net v0.39.0 h1:ZCu7HMWDxpXpaiKdhzIfaltL9Lp31x/3fCP11bc6/fY=\ngolang.org/x/net v0.39.0/go.mod h1:X7NRbYVEA+ewNkCNyJ513WmMdQ3BineSwVtN2zD/d+E=\ngolang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=\ngolang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=\ngolang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=\ngolang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=\ngolang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=\ngolang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=\ngolang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\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-20190227155943-e225da77a7e6/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-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20201207232520-09787c993a3a/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.13.0 h1:AauUjRAJ9OSnvULf/ARrrVywoJDy0YS2AwQ98I37610=\ngolang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=\ngolang.org/x/sys v0.0.0-20180830151530-49385e6e1522/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-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\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-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200803210538-64077c9b5642/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-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20=\ngolang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=\ngolang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=\ngolang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=\ngolang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/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.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\ngolang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\ngolang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=\ngolang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0=\ngolang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU=\ngolang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=\ngolang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=\ngolang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=\ngolang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=\ngolang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=\ngolang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=\ngolang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=\ngolang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=\ngolang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=\ngolang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=\ngolang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=\ngolang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=\ngolang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=\ngolang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=\ngolang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/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-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=\ngolang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=\ngolang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=\ngolang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=\ngolang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=\ngolang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=\ngolang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=\ngolang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=\ngolang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=\ngolang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=\ngolang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=\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=\ngoogle.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=\ngoogle.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=\ngoogle.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=\ngoogle.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=\ngoogle.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=\ngoogle.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=\ngoogle.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=\ngoogle.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=\ngoogle.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=\ngoogle.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=\ngoogle.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=\ngoogle.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=\ngoogle.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=\ngoogle.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=\ngoogle.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM=\ngoogle.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc=\ngoogle.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=\ngoogle.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=\ngoogle.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=\ngoogle.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=\ngoogle.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=\ngoogle.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=\ngoogle.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=\ngoogle.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=\ngoogle.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=\ngoogle.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=\ngoogle.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=\ngoogle.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=\ngoogle.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=\ngoogle.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=\ngoogle.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=\ngoogle.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=\ngoogle.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=\ngoogle.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=\ngoogle.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=\ngoogle.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=\ngoogle.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA=\ngoogle.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U=\ngoogle.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=\ngoogle.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=\ngoogle.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=\ngoogle.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=\ngoogle.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=\ngoogle.golang.org/genproto v0.0.0-20220504150022-98cd25cafc72/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4=\ngoogle.golang.org/genproto v0.0.0-20231212172506-995d672761c0 h1:YJ5pD9rF8o9Qtta0Cmy9rdBwkSjrTCT6XTiUQVOtIos=\ngoogle.golang.org/genproto v0.0.0-20231212172506-995d672761c0/go.mod h1:l/k7rMz0vFTBPy+tFSGvXEd3z+BcoG1k7EHbqm+YBsY=\ngoogle.golang.org/genproto/googleapis/api v0.0.0-20240102182953-50ed04b92917 h1:rcS6EyEaoCO52hQDupoSfrxI3R6C2Tq741is7X8OvnM=\ngoogle.golang.org/genproto/googleapis/api v0.0.0-20240102182953-50ed04b92917/go.mod h1:CmlNWB9lSezaYELKS5Ym1r44VrrbPUa7JTvw+6MbpJ0=\ngoogle.golang.org/genproto/googleapis/rpc v0.0.0-20240102182953-50ed04b92917 h1:6G8oQ016D88m1xAKljMlBOOGWDZkes4kMhgGFlf8WcQ=\ngoogle.golang.org/genproto/googleapis/rpc v0.0.0-20240102182953-50ed04b92917/go.mod h1:xtjpI3tXFPP051KaWnhvxkiubL/6dJ18vLVf7q2pTOU=\ngoogle.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=\ngoogle.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=\ngoogle.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=\ngoogle.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=\ngoogle.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=\ngoogle.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=\ngoogle.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=\ngoogle.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=\ngoogle.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60=\ngoogle.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=\ngoogle.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=\ngoogle.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=\ngoogle.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=\ngoogle.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=\ngoogle.golang.org/grpc v1.46.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=\ngoogle.golang.org/grpc v1.46.2/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=\ngoogle.golang.org/grpc v1.61.1 h1:kLAiWrZs7YeDM6MumDe7m3y4aM6wacLzM1Y/wiLP9XY=\ngoogle.golang.org/grpc v1.61.1/go.mod h1:VUbo7IFqmF1QtCAstipjG0GIoq49KvMe9+h1jFLBNJs=\ngoogle.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=\ngoogle.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=\ngoogle.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=\ngoogle.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=\ngoogle.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=\ngoogle.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=\ngoogle.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=\ngoogle.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=\ngoogle.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=\ngoogle.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=\ngoogle.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=\ngoogle.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=\ngoogle.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=\ngoogle.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=\ngoogle.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=\ngoogle.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=\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-20180628173108-788fd7840127/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/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=\ngopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8=\ngopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=\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.3/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.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=\ngopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=\ngopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\ngopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\ngopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=\ngopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\nhonnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\nhonnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\nhonnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\nhonnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\nhonnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=\nhonnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=\nhonnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=\nrsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=\nrsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=\nrsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=\n"
  },
  {
    "path": "contrib/polaris/limiter.go",
    "content": "package polaris\n\nimport (\n\t\"time\"\n\n\t\"github.com/go-kratos/aegis/ratelimit\"\n\n\t\"github.com/polarismesh/polaris-go\"\n\t\"github.com/polarismesh/polaris-go/pkg/model\"\n)\n\ntype (\n\t// LimiterOption function for polaris limiter\n\tLimiterOption func(*limiterOptions)\n)\n\ntype limiterOptions struct {\n\t// required, polaris limit namespace\n\tnamespace string\n\n\t// required, polaris limit service name\n\tservice string\n\n\t// optional, polaris limit request timeout\n\t// max value is (1+RetryCount) * Timeout\n\ttimeout time.Duration\n\n\t// optional, polaris limit retryCount\n\t// init by polaris config\n\tretryCount int\n\n\t// optional, request limit quota\n\ttoken uint32\n}\n\n// WithLimiterNamespace with limiter namespace.\nfunc WithLimiterNamespace(namespace string) LimiterOption {\n\treturn func(o *limiterOptions) {\n\t\to.namespace = namespace\n\t}\n}\n\n// WithLimiterService with limiter service.\nfunc WithLimiterService(service string) LimiterOption {\n\treturn func(o *limiterOptions) {\n\t\to.service = service\n\t}\n}\n\n// WithLimiterTimeout with limiter arguments.\nfunc WithLimiterTimeout(timeout time.Duration) LimiterOption {\n\treturn func(o *limiterOptions) {\n\t\to.timeout = timeout\n\t}\n}\n\n// WithLimiterRetryCount with limiter retryCount.\nfunc WithLimiterRetryCount(retryCount int) LimiterOption {\n\treturn func(o *limiterOptions) {\n\t\to.retryCount = retryCount\n\t}\n}\n\n// WithLimiterToken with limiter token.\nfunc WithLimiterToken(token uint32) LimiterOption {\n\treturn func(o *limiterOptions) {\n\t\to.token = token\n\t}\n}\n\ntype Limiter struct {\n\t// polaris limit api\n\tlimitAPI polaris.LimitAPI\n\n\topts limiterOptions\n}\n\n// init quotaRequest\nfunc buildRequest(opts limiterOptions) polaris.QuotaRequest {\n\tquotaRequest := polaris.NewQuotaRequest()\n\tquotaRequest.SetNamespace(opts.namespace)\n\tquotaRequest.SetRetryCount(opts.retryCount)\n\tquotaRequest.SetService(opts.service)\n\tquotaRequest.SetTimeout(opts.timeout)\n\tquotaRequest.SetToken(opts.token)\n\treturn quotaRequest\n}\n\n// Allow interface impl\nfunc (l *Limiter) Allow(method string, argument ...model.Argument) (ratelimit.DoneFunc, error) {\n\trequest := buildRequest(l.opts)\n\trequest.SetMethod(method)\n\tfor _, arg := range argument {\n\t\trequest.AddArgument(arg)\n\t}\n\tresp, err := l.limitAPI.GetQuota(request)\n\tif err != nil {\n\t\t// ignore err\n\t\treturn func(ratelimit.DoneInfo) {}, nil\n\t}\n\tif resp.Get().Code == model.QuotaResultOk {\n\t\treturn func(ratelimit.DoneInfo) {}, nil\n\t}\n\treturn nil, ratelimit.ErrLimitExceed\n}\n"
  },
  {
    "path": "contrib/polaris/polaris.go",
    "content": "package polaris\n\nimport (\n\t\"errors\"\n\n\t\"github.com/polarismesh/polaris-go\"\n\t\"github.com/polarismesh/polaris-go/api\"\n\n\t\"github.com/go-kratos/kratos/v2/config\"\n)\n\ntype Polaris struct {\n\trouter    polaris.RouterAPI\n\tconfig    polaris.ConfigAPI\n\tlimit     polaris.LimitAPI\n\tregistry  polaris.ProviderAPI\n\tdiscovery polaris.ConsumerAPI\n\tnamespace string\n\tservice   string\n}\n\n// Option is polaris option.\ntype Option func(o *Polaris)\n\n// WithNamespace with polaris global testNamespace\nfunc WithNamespace(ns string) Option {\n\treturn func(o *Polaris) {\n\t\to.namespace = ns\n\t}\n}\n\n// WithService set the current service name\nfunc WithService(service string) Option {\n\treturn func(o *Polaris) {\n\t\to.service = service\n\t}\n}\n\n// New polaris Service governance.\nfunc New(sdk api.SDKContext, opts ...Option) Polaris {\n\top := Polaris{\n\t\trouter:    polaris.NewRouterAPIByContext(sdk),\n\t\tconfig:    polaris.NewConfigAPIByContext(sdk),\n\t\tlimit:     polaris.NewLimitAPIByContext(sdk),\n\t\tregistry:  polaris.NewProviderAPIByContext(sdk),\n\t\tdiscovery: polaris.NewConsumerAPIByContext(sdk),\n\t\tnamespace: \"default\",\n\t}\n\tfor _, option := range opts {\n\t\toption(&op)\n\t}\n\treturn op\n}\n\nfunc (p *Polaris) Config(opts ...ConfigOption) (config.Source, error) {\n\toptions := &configOptions{\n\t\tnamespace: p.namespace,\n\t}\n\n\tfor _, opt := range opts {\n\t\topt(options)\n\t}\n\n\tif len(options.files) == 0 {\n\t\treturn nil, errors.New(\"fileNames invalid\")\n\t}\n\n\treturn &source{\n\t\tclient:  p.config,\n\t\toptions: options,\n\t}, nil\n}\n\nfunc (p *Polaris) Registry(opts ...RegistryOption) (r *Registry) {\n\top := registryOptions{\n\t\tNamespace: p.namespace,\n\t\tHealthy:   true,\n\t}\n\tfor _, option := range opts {\n\t\toption(&op)\n\t}\n\treturn &Registry{\n\t\topt:      op,\n\t\tprovider: p.registry,\n\t\tconsumer: p.discovery,\n\t}\n}\n\nfunc (p *Polaris) Limiter(opts ...LimiterOption) (r Limiter) {\n\top := limiterOptions{\n\t\tnamespace: p.namespace,\n\t\tservice:   p.service,\n\t}\n\tfor _, option := range opts {\n\t\toption(&op)\n\t}\n\treturn Limiter{\n\t\tlimitAPI: p.limit,\n\t\topts:     op,\n\t}\n}\n"
  },
  {
    "path": "contrib/polaris/ratelimit.go",
    "content": "package polaris\n\nimport (\n\t\"context\"\n\t\"strings\"\n\n\t\"github.com/go-kratos/aegis/ratelimit\"\n\n\t\"github.com/go-kratos/kratos/v2/errors\"\n\t\"github.com/go-kratos/kratos/v2/middleware\"\n\t\"github.com/go-kratos/kratos/v2/transport\"\n\t\"github.com/go-kratos/kratos/v2/transport/http\"\n\n\t\"github.com/polarismesh/polaris-go/pkg/model\"\n)\n\n// ErrLimitExceed is service unavailable due to rate limit exceeded.\nvar (\n\tErrLimitExceed = errors.New(429, \"RATELIMIT\", \"service unavailable due to rate limit exceeded\")\n)\n\n// Ratelimit Request rate limit middleware\nfunc Ratelimit(l Limiter) middleware.Middleware {\n\treturn func(handler middleware.Handler) middleware.Handler {\n\t\treturn func(ctx context.Context, req any) (reply any, err error) {\n\t\t\tif tr, ok := transport.FromServerContext(ctx); ok {\n\t\t\t\tvar args []model.Argument\n\t\t\t\theaders := tr.RequestHeader()\n\t\t\t\t// handle header\n\t\t\t\tfor _, header := range headers.Keys() {\n\t\t\t\t\targs = append(args, model.BuildHeaderArgument(header, headers.Get(header)))\n\t\t\t\t}\n\t\t\t\t// handle http\n\t\t\t\tif ht, ok := tr.(*http.Transport); ok {\n\t\t\t\t\t// url query\n\t\t\t\t\tfor key, values := range ht.Request().URL.Query() {\n\t\t\t\t\t\targs = append(args, model.BuildQueryArgument(key, strings.Join(values, \",\")))\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tdone, e := l.Allow(tr.Operation(), args...)\n\t\t\t\tif e != nil {\n\t\t\t\t\t// rejected\n\t\t\t\t\treturn nil, ErrLimitExceed\n\t\t\t\t}\n\t\t\t\t// allowed\n\t\t\t\treply, err = handler(ctx, req)\n\t\t\t\tdone(ratelimit.DoneInfo{Err: err})\n\t\t\t\treturn\n\t\t\t}\n\t\t\treturn reply, nil\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "contrib/polaris/registry.go",
    "content": "package polaris\n\nimport (\n\t\"context\"\n\t\"net\"\n\t\"net/url\"\n\t\"strconv\"\n\t\"time\"\n\n\t\"github.com/google/uuid\"\n\t\"github.com/polarismesh/polaris-go\"\n\t\"github.com/polarismesh/polaris-go/pkg/model\"\n\n\t\"github.com/go-kratos/kratos/v2/registry\"\n)\n\nvar (\n\t_ registry.Registrar = (*Registry)(nil)\n\t_ registry.Discovery = (*Registry)(nil)\n)\n\ntype registryOptions struct {\n\t// required, testNamespace in polaris\n\tNamespace string\n\n\t// required, service access token\n\tServiceToken string\n\n\t// service weight in polaris. Default value is 100, 0 <= weight <= 10000\n\tWeight int\n\n\t// service priority. Default value is 0. The smaller the value, the lower the priority\n\tPriority int\n\n\t// To show service is healthy or not. Default value is True .\n\tHealthy bool\n\n\t// To show service is isolate or not. Default value is False .\n\tIsolate bool\n\n\t// TTL timeout. if node needs to use heartbeat to report,required. If not set,server will throw ErrorCode-400141\n\tTTL int\n\n\t// optional, Timeout for single query. Default value is global config\n\t// Total is (1+RetryCount) * Timeout\n\tTimeout time.Duration\n\n\t// optional, retry count. Default value is global config\n\tRetryCount int\n}\n\n// RegistryOption is polaris option.\ntype RegistryOption func(o *registryOptions)\n\n// Registry is polaris registry.\ntype Registry struct {\n\topt      registryOptions\n\tprovider polaris.ProviderAPI\n\tconsumer polaris.ConsumerAPI\n}\n\n// WithRegistryServiceToken with ServiceToken option.\nfunc WithRegistryServiceToken(serviceToken string) RegistryOption {\n\treturn func(o *registryOptions) { o.ServiceToken = serviceToken }\n}\n\n// WithRegistryWeight with Weight option.\nfunc WithRegistryWeight(weight int) RegistryOption {\n\treturn func(o *registryOptions) { o.Weight = weight }\n}\n\n// WithRegistryHealthy with Healthy option.\nfunc WithRegistryHealthy(healthy bool) RegistryOption {\n\treturn func(o *registryOptions) { o.Healthy = healthy }\n}\n\n// WithRegistryIsolate with Isolate option.\nfunc WithRegistryIsolate(isolate bool) RegistryOption {\n\treturn func(o *registryOptions) { o.Isolate = isolate }\n}\n\n// WithRegistryTTL with TTL option.\nfunc WithRegistryTTL(TTL int) RegistryOption {\n\treturn func(o *registryOptions) { o.TTL = TTL }\n}\n\n// WithRegistryTimeout with Timeout option.\nfunc WithRegistryTimeout(timeout time.Duration) RegistryOption {\n\treturn func(o *registryOptions) { o.Timeout = timeout }\n}\n\n// WithRegistryRetryCount with RetryCount option.\nfunc WithRegistryRetryCount(retryCount int) RegistryOption {\n\treturn func(o *registryOptions) { o.RetryCount = retryCount }\n}\n\n// Register the registration.\nfunc (r *Registry) Register(_ context.Context, instance *registry.ServiceInstance) error {\n\tid := uuid.NewString()\n\tfor _, endpoint := range instance.Endpoints {\n\t\tu, err := url.Parse(endpoint)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\thost, port, err := net.SplitHostPort(u.Host)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tportNum, err := strconv.Atoi(port)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\t// metadata\n\t\trmd := mapClone(instance.Metadata)\n\t\tif rmd == nil {\n\t\t\trmd = make(map[string]string)\n\t\t}\n\t\trmd[\"merge\"] = id\n\t\tif _, ok := rmd[\"weight\"]; !ok {\n\t\t\trmd[\"weight\"] = strconv.Itoa(r.opt.Weight)\n\t\t}\n\n\t\tweight, _ := strconv.Atoi(rmd[\"weight\"])\n\n\t\t_, err = r.provider.RegisterInstance(\n\t\t\t&polaris.InstanceRegisterRequest{\n\t\t\t\tInstanceRegisterRequest: model.InstanceRegisterRequest{\n\t\t\t\t\tService:      instance.Name,\n\t\t\t\t\tServiceToken: r.opt.ServiceToken,\n\t\t\t\t\tNamespace:    r.opt.Namespace,\n\t\t\t\t\tHost:         host,\n\t\t\t\t\tPort:         portNum,\n\t\t\t\t\tProtocol:     &u.Scheme,\n\t\t\t\t\tWeight:       &weight,\n\t\t\t\t\tPriority:     &r.opt.Priority,\n\t\t\t\t\tVersion:      &instance.Version,\n\t\t\t\t\tMetadata:     rmd,\n\t\t\t\t\tHealthy:      &r.opt.Healthy,\n\t\t\t\t\tIsolate:      &r.opt.Isolate,\n\t\t\t\t\tTTL:          &r.opt.TTL,\n\t\t\t\t\tTimeout:      &r.opt.Timeout,\n\t\t\t\t\tRetryCount:   &r.opt.RetryCount,\n\t\t\t\t},\n\t\t\t},\n\t\t)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n\n// Deregister the registration.\nfunc (r *Registry) Deregister(_ context.Context, serviceInstance *registry.ServiceInstance) error {\n\tfor _, endpoint := range serviceInstance.Endpoints {\n\t\t// get url\n\t\tu, err := url.Parse(endpoint)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\t// get host and port\n\t\thost, port, err := net.SplitHostPort(u.Host)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\t// port to int\n\t\tportNum, err := strconv.Atoi(port)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\t// Deregister\n\t\terr = r.provider.Deregister(\n\t\t\t&polaris.InstanceDeRegisterRequest{\n\t\t\t\tInstanceDeRegisterRequest: model.InstanceDeRegisterRequest{\n\t\t\t\t\tService:      serviceInstance.Name,\n\t\t\t\t\tServiceToken: r.opt.ServiceToken,\n\t\t\t\t\tNamespace:    r.opt.Namespace,\n\t\t\t\t\tHost:         host,\n\t\t\t\t\tPort:         portNum,\n\t\t\t\t\tTimeout:      &r.opt.Timeout,\n\t\t\t\t\tRetryCount:   &r.opt.RetryCount,\n\t\t\t\t},\n\t\t\t},\n\t\t)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n\n// GetService return the service instances in memory according to the service name.\nfunc (r *Registry) GetService(_ context.Context, serviceName string) ([]*registry.ServiceInstance, error) {\n\t// get all instances\n\tinstancesResponse, err := r.consumer.GetInstances(&polaris.GetInstancesRequest{\n\t\tGetInstancesRequest: model.GetInstancesRequest{\n\t\t\tService:         serviceName,\n\t\t\tNamespace:       r.opt.Namespace,\n\t\t\tTimeout:         &r.opt.Timeout,\n\t\t\tRetryCount:      &r.opt.RetryCount,\n\t\t\tSkipRouteFilter: true,\n\t\t},\n\t})\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tserviceInstances := instancesToServiceInstances(merge(instancesResponse.GetInstances()))\n\n\treturn serviceInstances, nil\n}\n\nfunc merge(instances []model.Instance) map[string][]model.Instance {\n\tm := make(map[string][]model.Instance)\n\tfor _, instance := range instances {\n\t\tif v, ok := m[instance.GetMetadata()[\"merge\"]]; ok {\n\t\t\tm[instance.GetMetadata()[\"merge\"]] = append(v, instance)\n\t\t} else {\n\t\t\tm[instance.GetMetadata()[\"merge\"]] = []model.Instance{instance}\n\t\t}\n\t}\n\treturn m\n}\n\n// Watch creates a watcher according to the service name.\nfunc (r *Registry) Watch(ctx context.Context, serviceName string) (registry.Watcher, error) {\n\treturn newWatcher(ctx, r.opt.Namespace, serviceName, r.consumer)\n}\n\ntype Watcher struct {\n\tServiceName      string\n\tNamespace        string\n\tCtx              context.Context\n\tCancel           context.CancelFunc\n\tChannel          <-chan model.SubScribeEvent\n\tservice          *model.InstancesResponse\n\tServiceInstances map[string][]model.Instance\n\tfirst            bool\n}\n\nfunc newWatcher(ctx context.Context, namespace string, serviceName string, consumer polaris.ConsumerAPI) (*Watcher, error) {\n\twatchServiceResponse, err := consumer.WatchService(&polaris.WatchServiceRequest{\n\t\tWatchServiceRequest: model.WatchServiceRequest{\n\t\t\tKey: model.ServiceKey{\n\t\t\t\tNamespace: namespace,\n\t\t\t\tService:   serviceName,\n\t\t\t},\n\t\t},\n\t})\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tw := &Watcher{\n\t\tNamespace:        namespace,\n\t\tServiceName:      serviceName,\n\t\tChannel:          watchServiceResponse.EventChannel,\n\t\tservice:          watchServiceResponse.GetAllInstancesResp,\n\t\tServiceInstances: merge(watchServiceResponse.GetAllInstancesResp.GetInstances()),\n\t}\n\tw.Ctx, w.Cancel = context.WithCancel(ctx)\n\treturn w, nil\n}\n\n// Next returns services in the following two cases:\n// 1.the first time to watch and the service instance list is not empty.\n// 2.any service instance changes found.\n// if the above two conditions are not met, it will block until context deadline exceeded or canceled\nfunc (w *Watcher) Next() ([]*registry.ServiceInstance, error) {\n\tif !w.first {\n\t\tw.first = true\n\t\tif len(w.ServiceInstances) > 0 {\n\t\t\treturn instancesToServiceInstances(w.ServiceInstances), nil\n\t\t}\n\t}\n\tselect {\n\tcase <-w.Ctx.Done():\n\t\treturn nil, w.Ctx.Err()\n\tcase event := <-w.Channel:\n\t\tif event.GetSubScribeEventType() == model.EventInstance {\n\t\t\t// this always true, but we need to check it to make sure EventType not change\n\t\t\tif instanceEvent, ok := event.(*model.InstanceEvent); ok {\n\t\t\t\t// handle DeleteEvent\n\t\t\t\tif instanceEvent.DeleteEvent != nil {\n\t\t\t\t\tfor _, instance := range instanceEvent.DeleteEvent.Instances {\n\t\t\t\t\t\tdelete(w.ServiceInstances, instance.GetMetadata()[\"merge\"])\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t// handle UpdateEvent\n\t\t\t\tif instanceEvent.UpdateEvent != nil {\n\t\t\t\t\tfor _, update := range instanceEvent.UpdateEvent.UpdateList {\n\t\t\t\t\t\tif v, ok := w.ServiceInstances[update.After.GetMetadata()[\"merge\"]]; ok {\n\t\t\t\t\t\t\tvar nv []model.Instance\n\t\t\t\t\t\t\tm := map[string]model.Instance{}\n\t\t\t\t\t\t\tfor _, ins := range v {\n\t\t\t\t\t\t\t\tm[ins.GetId()] = ins\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tm[update.After.GetId()] = update.After\n\t\t\t\t\t\t\tfor _, ins := range m {\n\t\t\t\t\t\t\t\tif ins.IsHealthy() {\n\t\t\t\t\t\t\t\t\tnv = append(nv, ins)\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tw.ServiceInstances[update.After.GetMetadata()[\"merge\"]] = nv\n\t\t\t\t\t\t\tif len(nv) == 0 {\n\t\t\t\t\t\t\t\tdelete(w.ServiceInstances, update.After.GetMetadata()[\"merge\"])\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tif update.After.IsHealthy() {\n\t\t\t\t\t\t\t\tw.ServiceInstances[update.After.GetMetadata()[\"merge\"]] = []model.Instance{update.After}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t// handle AddEvent\n\t\t\t\tif instanceEvent.AddEvent != nil {\n\t\t\t\t\tfor _, instance := range instanceEvent.AddEvent.Instances {\n\t\t\t\t\t\tif v, ok := w.ServiceInstances[instance.GetMetadata()[\"merge\"]]; ok {\n\t\t\t\t\t\t\tvar nv []model.Instance\n\t\t\t\t\t\t\tm := map[string]model.Instance{}\n\t\t\t\t\t\t\tfor _, ins := range v {\n\t\t\t\t\t\t\t\tm[ins.GetId()] = ins\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tm[instance.GetId()] = instance\n\t\t\t\t\t\t\tfor _, ins := range m {\n\t\t\t\t\t\t\t\tif ins.IsHealthy() {\n\t\t\t\t\t\t\t\t\tnv = append(nv, ins)\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif len(nv) != 0 {\n\t\t\t\t\t\t\t\tw.ServiceInstances[instance.GetMetadata()[\"merge\"]] = nv\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tif instance.IsHealthy() {\n\t\t\t\t\t\t\t\tw.ServiceInstances[instance.GetMetadata()[\"merge\"]] = []model.Instance{instance}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn instancesToServiceInstances(w.ServiceInstances), nil\n\t\t}\n\t}\n\treturn instancesToServiceInstances(w.ServiceInstances), nil\n}\n\n// Stop close the watcher.\nfunc (w *Watcher) Stop() error {\n\tw.Cancel()\n\treturn nil\n}\n\nfunc instancesToServiceInstances(instances map[string][]model.Instance) []*registry.ServiceInstance {\n\tserviceInstances := make([]*registry.ServiceInstance, 0, len(instances))\n\tfor _, inss := range instances {\n\t\tif len(inss) == 0 {\n\t\t\tcontinue\n\t\t}\n\t\tins := &registry.ServiceInstance{\n\t\t\tID:       inss[0].GetId(),\n\t\t\tName:     inss[0].GetService(),\n\t\t\tVersion:  inss[0].GetVersion(),\n\t\t\tMetadata: inss[0].GetMetadata(),\n\t\t}\n\t\tfor _, item := range inss {\n\t\t\tif item.IsHealthy() {\n\t\t\t\tins.Endpoints = append(ins.Endpoints, item.GetProtocol()+\"://\"+net.JoinHostPort(item.GetHost(), strconv.FormatUint(uint64(item.GetPort()), 10)))\n\t\t\t}\n\t\t}\n\t\tif len(ins.Endpoints) != 0 {\n\t\t\tserviceInstances = append(serviceInstances, ins)\n\t\t}\n\t}\n\treturn serviceInstances\n}\n\n// Clone returns a copy of m. This is a shallow clone:\n// the new keys and values are set using ordinary assignment.\nfunc mapClone[M ~map[K]V, K comparable, V any](m M) M {\n\t// Preserve nil in case it matters.\n\tif m == nil {\n\t\treturn nil\n\t}\n\t// Make a shallow copy of the map.\n\tm2 := make(M, len(m))\n\tfor k, v := range m {\n\t\tm2[k] = v\n\t}\n\treturn m2\n}\n"
  },
  {
    "path": "contrib/polaris/registry_test.go",
    "content": "package polaris\n\nimport (\n\t\"context\"\n\t\"strconv\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/polarismesh/polaris-go\"\n\n\t\"github.com/go-kratos/kratos/v2/registry\"\n)\n\n// TestRegistry\nfunc TestRegistry(t *testing.T) {\n\tsdk, err := polaris.NewSDKContextByAddress(\"127.0.0.1:8091\")\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tp := New(sdk)\n\n\tr := p.Registry(\n\t\tWithRegistryTimeout(time.Second),\n\t\tWithRegistryHealthy(true),\n\t\tWithRegistryIsolate(false),\n\t\tWithRegistryRetryCount(3),\n\t\tWithRegistryWeight(100),\n\t\tWithRegistryTTL(1000),\n\t)\n\n\tmm := map[string]string{\n\t\t\"test1\": \"test1\",\n\t}\n\tins := &registry.ServiceInstance{\n\t\tID:      \"test-ut\",\n\t\tName:    \"test-ut\",\n\t\tVersion: \"v1.0.0\",\n\t\tEndpoints: []string{\n\t\t\t\"grpc://127.0.0.1:8080\",\n\t\t\t\"http://127.0.0.1:9090\",\n\t\t},\n\t\tMetadata: mm,\n\t}\n\n\tgo func() {\n\t\tfor i := 0; true; i++ {\n\t\t\tstr := \"test\" + strconv.Itoa(i)\n\t\t\t_ = mm[str]\n\t\t\tif i > 100 {\n\t\t\t\ti = 0\n\t\t\t}\n\t\t}\n\t}()\n\n\terr = r.Register(context.Background(), ins)\n\n\tt.Cleanup(func() {\n\t\tif err = r.Deregister(context.Background(), ins); err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\t})\n\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\ttime.Sleep(time.Second * 3)\n\tservice, err := r.GetService(context.Background(), \"test-ut\")\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tt.Log(service)\n}\n"
  },
  {
    "path": "contrib/polaris/router.go",
    "content": "package polaris\n\nimport (\n\t\"context\"\n\t\"net\"\n\t\"strconv\"\n\t\"strings\"\n\n\t\"github.com/polarismesh/polaris-go\"\n\t\"github.com/polarismesh/polaris-go/pkg/model\"\n\t\"github.com/polarismesh/polaris-go/pkg/model/local\"\n\t\"github.com/polarismesh/polaris-go/pkg/model/pb\"\n\tv1 \"github.com/polarismesh/polaris-go/pkg/model/pb/v1\"\n\t\"google.golang.org/protobuf/types/known/wrapperspb\"\n\n\t\"github.com/go-kratos/kratos/v2\"\n\t\"github.com/go-kratos/kratos/v2/log\"\n\t\"github.com/go-kratos/kratos/v2/selector\"\n\t\"github.com/go-kratos/kratos/v2/transport\"\n\t\"github.com/go-kratos/kratos/v2/transport/http\"\n)\n\ntype router struct {\n\tservice string\n}\n\ntype RouterOption func(o *router)\n\n// WithRouterService set the caller service name used by the route\nfunc WithRouterService(service string) RouterOption {\n\treturn func(o *router) {\n\t\to.service = service\n\t}\n}\n\n// NodeFilter polaris dynamic router selector\nfunc (p *Polaris) NodeFilter(opts ...RouterOption) selector.NodeFilter {\n\to := router{service: p.service}\n\tfor _, opt := range opts {\n\t\topt(&o)\n\t}\n\treturn func(ctx context.Context, nodes []selector.Node) []selector.Node {\n\t\tif len(nodes) == 0 {\n\t\t\treturn nodes\n\t\t}\n\t\treq := &polaris.ProcessRoutersRequest{\n\t\t\tProcessRoutersRequest: model.ProcessRoutersRequest{\n\t\t\t\tSourceService: model.ServiceInfo{Namespace: p.namespace, Service: o.service},\n\t\t\t\tDstInstances:  buildPolarisInstance(p.namespace, nodes),\n\t\t\t},\n\t\t}\n\t\tif appInfo, ok := kratos.FromContext(ctx); ok {\n\t\t\treq.SourceService.Service = appInfo.Name()\n\t\t}\n\n\t\treq.AddArguments(model.BuildCallerServiceArgument(p.namespace, req.SourceService.Service))\n\n\t\t// process transport\n\t\tif tr, ok := transport.FromClientContext(ctx); ok {\n\t\t\treq.AddArguments(model.BuildMethodArgument(tr.Operation()))\n\t\t\treq.AddArguments(model.BuildPathArgument(tr.Operation()))\n\n\t\t\tfor _, key := range tr.RequestHeader().Keys() {\n\t\t\t\treq.AddArguments(model.BuildHeaderArgument(strings.ToLower(key), tr.RequestHeader().Get(key)))\n\t\t\t}\n\n\t\t\t// http\n\t\t\tif ht, ok := tr.(http.Transporter); ok {\n\t\t\t\treq.AddArguments(model.BuildPathArgument(ht.Request().URL.Path))\n\t\t\t\treq.AddArguments(model.BuildCallerIPArgument(ht.Request().RemoteAddr))\n\n\t\t\t\t// cookie\n\t\t\t\tfor _, cookie := range ht.Request().Cookies() {\n\t\t\t\t\treq.AddArguments(model.BuildCookieArgument(cookie.Name, cookie.Value))\n\t\t\t\t}\n\n\t\t\t\t// url query\n\t\t\t\tfor key, values := range ht.Request().URL.Query() {\n\t\t\t\t\treq.AddArguments(model.BuildQueryArgument(key, strings.Join(values, \",\")))\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tn := make(map[string]selector.Node, len(nodes))\n\t\tfor _, node := range nodes {\n\t\t\tn[node.Address()] = node\n\t\t}\n\n\t\tm, err := p.router.ProcessRouters(req)\n\t\tif err != nil {\n\t\t\tlog.Errorf(\"polaris process routers failed, err=%v\", err)\n\t\t\treturn nodes\n\t\t}\n\n\t\tnewNode := make([]selector.Node, 0, len(m.Instances))\n\t\tfor _, ins := range m.GetInstances() {\n\t\t\tif v, ok := n[net.JoinHostPort(ins.GetHost(), strconv.FormatUint(uint64(ins.GetPort()), 10))]; ok {\n\t\t\t\tnewNode = append(newNode, v)\n\t\t\t}\n\t\t}\n\t\tif len(newNode) == 0 {\n\t\t\treturn nodes\n\t\t}\n\t\treturn newNode\n\t}\n}\n\nfunc buildPolarisInstance(namespace string, nodes []selector.Node) *pb.ServiceInstancesInProto {\n\tins := make([]*v1.Instance, 0, len(nodes))\n\tfor _, node := range nodes {\n\t\thost, port, err := net.SplitHostPort(node.Address())\n\t\tif err != nil {\n\t\t\tlog.Errorf(\"split host port failed error: %v\", err)\n\t\t\treturn nil\n\t\t}\n\t\tportUint64, err := strconv.ParseUint(port, 10, 32)\n\t\tif err != nil {\n\t\t\tlog.Errorf(\"parse port failed error: %v\", err)\n\t\t\treturn nil\n\t\t}\n\t\tins = append(ins, &v1.Instance{\n\t\t\tId:        wrapperspb.String(node.Metadata()[\"merge\"]),\n\t\t\tService:   wrapperspb.String(node.ServiceName()),\n\t\t\tNamespace: wrapperspb.String(namespace),\n\t\t\tHost:      wrapperspb.String(host),\n\t\t\tPort:      wrapperspb.UInt32(uint32(portUint64)),\n\t\t\tProtocol:  wrapperspb.String(node.Scheme()),\n\t\t\tVersion:   wrapperspb.String(node.Version()),\n\t\t\tWeight:    wrapperspb.UInt32(uint32(*node.InitialWeight())),\n\t\t\tMetadata:  node.Metadata(),\n\t\t})\n\t}\n\n\td := &v1.DiscoverResponse{\n\t\tCode:      wrapperspb.UInt32(1),\n\t\tInfo:      wrapperspb.String(\"ok\"),\n\t\tType:      v1.DiscoverResponse_INSTANCE,\n\t\tService:   &v1.Service{Name: wrapperspb.String(nodes[0].ServiceName()), Namespace: wrapperspb.String(\"default\")},\n\t\tInstances: ins,\n\t}\n\treturn pb.NewServiceInstancesInProto(d, func(string) local.InstanceLocalValue {\n\t\treturn local.NewInstanceLocalValue()\n\t}, &pb.SvcPluginValues{Routers: nil, Loadbalancer: nil}, nil)\n}\n"
  },
  {
    "path": "contrib/polaris/router_test.go",
    "content": "package polaris\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/polarismesh/polaris-go\"\n\n\t\"github.com/go-kratos/kratos/v2\"\n\t\"github.com/go-kratos/kratos/v2/registry\"\n\t\"github.com/go-kratos/kratos/v2/selector\"\n)\n\nfunc TestRouter(t *testing.T) {\n\ttoken, err := getToken(\"http://127.0.0.1:8090\")\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tdata := `\n[\n    {\n        \"name\":\"kratos\",\n        \"enable\":false,\n        \"description\":\"123\",\n        \"priority\":2,\n        \"routing_config\":{\n            \"@type\":\"type.googleapis.com/v2.RuleRoutingConfig\",\n            \"sources\":[\n                {\n                    \"service\":\"*\",\n                    \"namespace\":\"*\",\n                    \"arguments\":[\n                    ]\n                }\n            ],\n            \"destinations\":[\n                {\n                    \"labels\":{\n                        \"az\":{\n                            \"value\":\"1\",\n                            \"value_type\":\"TEXT\",\n                            \"type\":\"EXACT\"\n                        }\n                    },\n                    \"weight\":100,\n                    \"priority\":1,\n                    \"isolate\":false,\n                    \"name\":\"实例分组1\",\n                    \"namespace\":\"default\",\n                    \"service\":\"test-ut\"\n                }\n            ]\n        }\n    }\n]\n`\n\tres, err := makeJSONRequest(\"http://127.0.0.1:8090/naming/v2/routings\", data, http.MethodPost, map[string]string{\n\t\t\"X-Polaris-Token\": token,\n\t})\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tresJSON := struct {\n\t\tCode      int `json:\"code\"`\n\t\tResponses []struct {\n\t\t\tData struct {\n\t\t\t\tID string `json:\"id\"`\n\t\t\t}\n\t\t} `json:\"responses\"`\n\t}{}\n\n\terr = json.Unmarshal(res, &resJSON)\n\tif err != nil {\n\t\tt.Fatal(err, string(res))\n\t}\n\tif resJSON.Code != 200000 {\n\t\tt.Fatal(\"create failed\", string(res))\n\t}\n\n\t// enable router\n\tenableData := fmt.Sprintf(`[{\"id\":\"%s\",\"enable\":true}]`, resJSON.Responses[0].Data.ID)\n\tres, err = makeJSONRequest(\"http://127.0.0.1:8090/naming/v2/routings/enable\", enableData, http.MethodPut, map[string]string{\n\t\t\"X-Polaris-Token\": token,\n\t})\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\terr = json.Unmarshal(res, &resJSON)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif resJSON.Code != 200000 {\n\t\tt.Fatal(\"enable failed\", string(res))\n\t}\n\n\tt.Cleanup(func() {\n\t\tenableData := fmt.Sprintf(`[{\"id\":\"%s\"}]`, resJSON.Responses[0].Data.ID)\n\t\tres, err = makeJSONRequest(\"http://127.0.0.1:8090/naming/v2/routings/delete\", enableData, http.MethodPost, map[string]string{\n\t\t\t\"X-Polaris-Token\": token,\n\t\t})\n\t\tresJSON := &commonRes{}\n\t\terr = json.Unmarshal(res, resJSON)\n\t\tif err != nil {\n\t\t\tt.Fatal(err, string(res))\n\t\t}\n\t\tif resJSON.Code != 200000 {\n\t\t\tt.Fatal(\"delete failed\", string(res))\n\t\t}\n\t})\n\n\tsdk, err := polaris.NewSDKContextByAddress(\"127.0.0.1:8091\")\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tp := New(sdk)\n\n\tr := p.Registry(\n\t\tWithRegistryTimeout(time.Second),\n\t\tWithRegistryHealthy(true),\n\t\tWithRegistryIsolate(false),\n\t\tWithRegistryRetryCount(0),\n\t\tWithRegistryWeight(100),\n\t\tWithRegistryTTL(10),\n\t)\n\n\tins := &registry.ServiceInstance{\n\t\tID:      \"kratos\",\n\t\tName:    \"kratos\",\n\t\tVersion: \"v1.0.0\",\n\t\tEndpoints: []string{\n\t\t\t\"grpc://127.0.0.1:8080\",\n\t\t\t\"http://127.0.0.1:9090\",\n\t\t},\n\t}\n\n\terr = r.Register(context.Background(), ins)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\ttime.Sleep(time.Second * 5)\n\tnodes := []selector.Node{\n\t\tselector.NewNode(\"grpc\", \"127.0.0.1:9000\", &registry.ServiceInstance{\n\t\t\tID:        \"123\",\n\t\t\tName:      \"test-ut\",\n\t\t\tVersion:   \"v1.0.0\",\n\t\t\tMetadata:  map[string]string{\"weight\": \"100\", \"az\": \"1\"},\n\t\t\tEndpoints: []string{\"grpc://127.0.0.1:9000\"},\n\t\t}),\n\t\tselector.NewNode(\"grpc\", \"127.0.0.2:9000\", &registry.ServiceInstance{\n\t\t\tID:        \"123\",\n\t\t\tName:      \"test-ut\",\n\t\t\tVersion:   \"v1.0.0\",\n\t\t\tMetadata:  map[string]string{\"weight\": \"100\", \"az\": \"2\"},\n\t\t\tEndpoints: []string{\"grpc://127.0.0.2:9000\"},\n\t\t}),\n\t\tselector.NewNode(\"grpc\", \"127.0.0.3:9000\", &registry.ServiceInstance{\n\t\t\tID:        \"123\",\n\t\t\tName:      \"test-ut\",\n\t\t\tVersion:   \"v1.0.0\",\n\t\t\tMetadata:  map[string]string{\"weight\": \"100\", \"az\": \"1\"},\n\t\t\tEndpoints: []string{\"grpc://127.0.0.3:9000\"},\n\t\t}),\n\t}\n\n\tf := p.NodeFilter()\n\tctx := kratos.NewContext(context.Background(), &mockApp{})\n\tn := f(ctx, nodes)\n\tfor _, node := range n {\n\t\tif node.Metadata()[\"az\"] != \"1\" {\n\t\t\tt.Fatal(\"node filter result wrong\")\n\t\t}\n\t\tt.Log(node)\n\t}\n\tif len(n) != 2 {\n\t\tt.Fatal(\"node filter result wrong\")\n\t}\n}\n\ntype mockApp struct{}\n\nfunc (m mockApp) ID() string {\n\treturn \"1\"\n}\n\nfunc (m mockApp) Name() string {\n\treturn \"kratos\"\n}\n\nfunc (m mockApp) Version() string {\n\treturn \"v2.0.0\"\n}\n\nfunc (m mockApp) Metadata() map[string]string {\n\treturn map[string]string{}\n}\n\nfunc (m mockApp) Endpoint() []string {\n\treturn []string{\"grpc://123.123.123.123:9090\"}\n}\n"
  },
  {
    "path": "contrib/registry/consul/client.go",
    "content": "package consul\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"math/rand/v2\"\n\t\"net\"\n\t\"net/url\"\n\t\"strconv\"\n\t\"strings\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/go-kratos/kratos/v2/log\"\n\t\"github.com/go-kratos/kratos/v2/registry\"\n\n\t\"github.com/hashicorp/consul/api\"\n)\n\ntype Datacenter string\n\nconst (\n\tSingleDatacenter Datacenter = \"SINGLE\"\n\tMultiDatacenter  Datacenter = \"MULTI\"\n)\n\n// Client is consul client config\ntype Client struct {\n\tdc  Datacenter\n\tcli *api.Client\n\n\t// resolve service entry endpoints\n\tresolver ServiceResolver\n\t// healthcheck time interval in seconds\n\thealthcheckInterval int\n\t// heartbeat enable heartbeat\n\theartbeat bool\n\t// deregisterCriticalServiceAfter time interval in seconds\n\tderegisterCriticalServiceAfter int\n\t// serviceChecks  user custom checks\n\tserviceChecks api.AgentServiceChecks\n\t// tags is service tags\n\ttags []string\n\n\t// used to control heartbeat\n\tlock      sync.RWMutex\n\tcancelers map[string]*canceler\n}\n\ntype canceler struct {\n\tctx    context.Context\n\tcancel context.CancelFunc\n\tdone   chan struct{}\n}\n\nfunc defaultResolver(_ context.Context, entries []*api.ServiceEntry) []*registry.ServiceInstance {\n\tservices := make([]*registry.ServiceInstance, 0, len(entries))\n\tfor _, entry := range entries {\n\t\tvar version string\n\t\tfor _, tag := range entry.Service.Tags {\n\t\t\tss := strings.SplitN(tag, \"=\", 2)\n\t\t\tif len(ss) == 2 && ss[0] == \"version\" {\n\t\t\t\tversion = ss[1]\n\t\t\t}\n\t\t}\n\t\tendpoints := make([]string, 0)\n\t\tfor scheme, addr := range entry.Service.TaggedAddresses {\n\t\t\tif scheme == \"lan_ipv4\" || scheme == \"wan_ipv4\" || scheme == \"lan_ipv6\" || scheme == \"wan_ipv6\" {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tendpoints = append(endpoints, addr.Address)\n\t\t}\n\t\tif len(endpoints) == 0 && entry.Service.Address != \"\" && entry.Service.Port != 0 {\n\t\t\tendpoints = append(endpoints, \"http://\"+net.JoinHostPort(entry.Service.Address, strconv.FormatUint(uint64(entry.Service.Port), 10)))\n\t\t}\n\t\tservices = append(services, &registry.ServiceInstance{\n\t\t\tID:        entry.Service.ID,\n\t\t\tName:      entry.Service.Service,\n\t\t\tMetadata:  entry.Service.Meta,\n\t\t\tVersion:   version,\n\t\t\tEndpoints: endpoints,\n\t\t})\n\t}\n\n\treturn services\n}\n\n// ServiceResolver is used to resolve service endpoints\ntype ServiceResolver func(ctx context.Context, entries []*api.ServiceEntry) []*registry.ServiceInstance\n\n// Service get services from consul\nfunc (c *Client) Service(ctx context.Context, service string, index uint64, passingOnly bool) ([]*registry.ServiceInstance, uint64, error) {\n\tif c.dc == MultiDatacenter {\n\t\treturn c.multiDCService(ctx, service, index, passingOnly)\n\t}\n\n\topts := &api.QueryOptions{\n\t\tWaitIndex:  index,\n\t\tWaitTime:   time.Second * 55,\n\t\tDatacenter: string(c.dc),\n\t}\n\topts = opts.WithContext(ctx)\n\n\tif c.dc == SingleDatacenter {\n\t\topts.Datacenter = \"\"\n\t}\n\n\tentries, meta, err := c.singleDCEntries(service, \"\", passingOnly, opts)\n\tif err != nil {\n\t\treturn nil, 0, err\n\t}\n\treturn c.resolver(ctx, entries), meta.LastIndex, nil\n}\n\nfunc (c *Client) multiDCService(ctx context.Context, service string, index uint64, passingOnly bool) ([]*registry.ServiceInstance, uint64, error) {\n\topts := &api.QueryOptions{\n\t\tWaitIndex: index,\n\t\tWaitTime:  time.Second * 55,\n\t}\n\topts = opts.WithContext(ctx)\n\n\tvar instances []*registry.ServiceInstance\n\n\tdcs, err := c.cli.Catalog().Datacenters()\n\tif err != nil {\n\t\treturn nil, 0, err\n\t}\n\n\tfor _, dc := range dcs {\n\t\topts.Datacenter = dc\n\t\te, m, err := c.singleDCEntries(service, \"\", passingOnly, opts)\n\t\tif err != nil {\n\t\t\treturn nil, 0, err\n\t\t}\n\n\t\tins := c.resolver(ctx, e)\n\t\tfor _, in := range ins {\n\t\t\tif in.Metadata == nil {\n\t\t\t\tin.Metadata = make(map[string]string, 1)\n\t\t\t}\n\t\t\tin.Metadata[\"dc\"] = dc\n\t\t}\n\n\t\tinstances = append(instances, ins...)\n\t\topts.WaitIndex = m.LastIndex\n\t}\n\n\treturn instances, opts.WaitIndex, nil\n}\n\nfunc (c *Client) singleDCEntries(service, tag string, passingOnly bool, opts *api.QueryOptions) ([]*api.ServiceEntry, *api.QueryMeta, error) {\n\treturn c.cli.Health().Service(service, tag, passingOnly, opts)\n}\n\n// Register register service instance to consul\nfunc (c *Client) Register(ctx context.Context, svc *registry.ServiceInstance, enableHealthCheck bool) error {\n\taddresses := make(map[string]api.ServiceAddress, len(svc.Endpoints))\n\tcheckAddresses := make([]string, 0, len(svc.Endpoints))\n\tfor _, endpoint := range svc.Endpoints {\n\t\traw, err := url.Parse(endpoint)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\taddr := raw.Hostname()\n\t\tport, _ := strconv.ParseUint(raw.Port(), 10, 16)\n\n\t\tcheckAddresses = append(checkAddresses, net.JoinHostPort(addr, strconv.FormatUint(port, 10)))\n\t\taddresses[raw.Scheme] = api.ServiceAddress{Address: endpoint, Port: int(port)}\n\t}\n\ttags := []string{fmt.Sprintf(\"version=%s\", svc.Version)}\n\tif len(c.tags) > 0 {\n\t\ttags = append(tags, c.tags...)\n\t}\n\tasr := &api.AgentServiceRegistration{\n\t\tID:              svc.ID,\n\t\tName:            svc.Name,\n\t\tMeta:            svc.Metadata,\n\t\tTags:            tags,\n\t\tTaggedAddresses: addresses,\n\t}\n\tif len(checkAddresses) > 0 {\n\t\thost, portRaw, _ := net.SplitHostPort(checkAddresses[0])\n\t\tport, _ := strconv.ParseInt(portRaw, 10, 32)\n\t\tasr.Address = host\n\t\tasr.Port = int(port)\n\t}\n\tif enableHealthCheck {\n\t\tfor _, address := range checkAddresses {\n\t\t\tasr.Checks = append(asr.Checks, &api.AgentServiceCheck{\n\t\t\t\tTCP:                            address,\n\t\t\t\tInterval:                       fmt.Sprintf(\"%ds\", c.healthcheckInterval),\n\t\t\t\tDeregisterCriticalServiceAfter: fmt.Sprintf(\"%ds\", c.deregisterCriticalServiceAfter),\n\t\t\t\tTimeout:                        \"5s\",\n\t\t\t})\n\t\t}\n\t\t// custom checks\n\t\tasr.Checks = append(asr.Checks, c.serviceChecks...)\n\t}\n\tif c.heartbeat {\n\t\tasr.Checks = append(asr.Checks, &api.AgentServiceCheck{\n\t\t\tCheckID:                        \"service:\" + svc.ID,\n\t\t\tTTL:                            fmt.Sprintf(\"%ds\", c.healthcheckInterval*2),\n\t\t\tDeregisterCriticalServiceAfter: fmt.Sprintf(\"%ds\", c.deregisterCriticalServiceAfter),\n\t\t})\n\t}\n\n\tc.lock.Lock()\n\tif cc, ok := c.cancelers[svc.ID]; ok {\n\t\tcc.cancel()\n\t\t<-cc.done\n\t}\n\tvar cc *canceler\n\tif c.heartbeat {\n\t\tcancelCtx, cancel := context.WithCancel(context.Background())\n\t\tcc = &canceler{\n\t\t\tctx:    cancelCtx,\n\t\t\tcancel: cancel,\n\t\t\tdone:   make(chan struct{}),\n\t\t}\n\t\tc.cancelers[svc.ID] = cc\n\t\tgo func() {\n\t\t\t<-cc.done\n\t\t\tcc.cancel()\n\t\t\tc.lock.Lock()\n\t\t\tif c.cancelers[svc.ID] == cc {\n\t\t\t\tdelete(c.cancelers, svc.ID)\n\t\t\t}\n\t\t\tc.lock.Unlock()\n\t\t}()\n\t}\n\tc.lock.Unlock()\n\n\terr := c.cli.Agent().ServiceRegisterOpts(asr, api.ServiceRegisterOpts{}.WithContext(ctx))\n\tif err != nil {\n\t\tif c.heartbeat {\n\t\t\tclose(cc.done)\n\t\t}\n\t\treturn err\n\t}\n\n\tif c.heartbeat {\n\t\tgo func() {\n\t\t\tdefer close(cc.done)\n\t\t\terr = c.cli.Agent().UpdateTTL(\"service:\"+svc.ID, \"pass\", \"pass\")\n\t\t\tif err != nil {\n\t\t\t\tlog.Errorf(\"[Consul]update ttl heartbeat to consul failed!err:=%v\", err)\n\t\t\t}\n\t\t\tticker := time.NewTicker(time.Second * time.Duration(c.healthcheckInterval))\n\t\t\tdefer ticker.Stop()\n\t\t\tfor {\n\t\t\t\tselect {\n\t\t\t\tcase <-cc.ctx.Done():\n\t\t\t\t\t_ = c.cli.Agent().ServiceDeregister(svc.ID)\n\t\t\t\t\treturn\n\t\t\t\tcase <-ticker.C:\n\t\t\t\t\terr = c.cli.Agent().UpdateTTLOpts(\"service:\"+svc.ID, \"pass\", \"pass\", new(api.QueryOptions).WithContext(cc.ctx))\n\t\t\t\t\tif errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) {\n\t\t\t\t\t\t_ = c.cli.Agent().ServiceDeregister(svc.ID)\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\tlog.Errorf(\"[Consul] update ttl heartbeat to consul failed! err=%v\", err)\n\t\t\t\t\t\t// when the previous report fails, try to re register the service\n\t\t\t\t\t\tif err := sleepCtx(cc.ctx, time.Duration(rand.IntN(5))*time.Second); err != nil {\n\t\t\t\t\t\t\t_ = c.cli.Agent().ServiceDeregister(svc.ID)\n\t\t\t\t\t\t\treturn\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif err := c.cli.Agent().ServiceRegisterOpts(asr, api.ServiceRegisterOpts{}.WithContext(cc.ctx)); err != nil {\n\t\t\t\t\t\t\tlog.Errorf(\"[Consul] re registry service failed!, err=%v\", err)\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tlog.Warn(\"[Consul] re registry of service occurred success\")\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}()\n\t}\n\treturn nil\n}\n\nfunc sleepCtx(ctx context.Context, d time.Duration) error {\n\tt := time.NewTimer(d)\n\tdefer t.Stop()\n\tselect {\n\tcase <-ctx.Done():\n\t\treturn ctx.Err()\n\tcase <-t.C:\n\t\treturn nil\n\t}\n}\n\n// Deregister service by service ID\nfunc (c *Client) Deregister(ctx context.Context, serviceID string) error {\n\tc.lock.RLock()\n\tcc, ok := c.cancelers[serviceID]\n\tc.lock.RUnlock()\n\tif ok {\n\t\tcc.cancel()\n\t\t<-cc.done\n\t}\n\n\terr := c.cli.Agent().ServiceDeregisterOpts(serviceID, new(api.QueryOptions).WithContext(ctx))\n\tvar se api.StatusError\n\tif errors.As(err, &se) && se.Code == 404 {\n\t\t// not found\n\t\terr = nil\n\t}\n\treturn err\n}\n"
  },
  {
    "path": "contrib/registry/consul/go.mod",
    "content": "module github.com/go-kratos/kratos/contrib/registry/consul/v2\n\ngo 1.22\n\nrequire (\n\tgithub.com/go-kratos/kratos/v2 v2.9.2\n\tgithub.com/hashicorp/consul/api v1.26.1\n)\n\nrequire (\n\tgithub.com/armon/go-metrics v0.4.1 // indirect\n\tgithub.com/fatih/color v1.14.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/mattn/go-colorable v0.1.13 // indirect\n\tgithub.com/mattn/go-isatty v0.0.17 // indirect\n\tgithub.com/mitchellh/go-homedir v1.1.0 // indirect\n\tgithub.com/mitchellh/mapstructure v1.5.0 // indirect\n\tgolang.org/x/exp v0.0.0-20230817173708-d852ddb80c63 // indirect\n\tgolang.org/x/sys v0.28.0 // indirect\n)\n\nreplace github.com/go-kratos/kratos/v2 => ../../../\n"
  },
  {
    "path": "contrib/registry/consul/go.sum",
    "content": "github.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/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.14.1 h1:qfhVLaG5s+nCROl1zJsZRxFeYrHLqWroPOQ8BWiNb4w=\ngithub.com/fatih/color v1.14.1/go.mod h1:2oHN61fhTpgcxD3TSWCgKDiH1+x4OiDVVGH8WlgGZGg=\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-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=\ngithub.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=\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/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.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=\ngithub.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=\ngithub.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=\ngithub.com/hashicorp/consul/api v1.26.1 h1:5oSXOO5fboPZeW5SN+TdGFP/BILDgBm19OrPZ/pICIM=\ngithub.com/hashicorp/consul/api v1.26.1/go.mod h1:B4sQTeaSO16NtynqrAdwOlahJ7IUDZM9cj2420xYL8A=\ngithub.com/hashicorp/consul/sdk v0.15.0 h1:2qK9nDrr4tiJKRoxPGhm6B7xJjLVIQqkjiab2M4aKjU=\ngithub.com/hashicorp/consul/sdk v0.15.0/go.mod h1:r/OmRRPbHOe0yxNahLw7G9x5WG17E1BIECMtCjcPSNo=\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/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=\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/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/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.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng=\ngithub.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=\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/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/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=\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/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/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=\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/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.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY=\ngithub.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=\ngithub.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM=\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/exp v0.0.0-20230817173708-d852ddb80c63 h1:m64FZMko/V45gv0bNmrNYoDEq8U5YUhetc9cBWKS1TQ=\ngolang.org/x/exp v0.0.0-20230817173708-d852ddb80c63/go.mod h1:0v4NqG35kSWCMzLaMeX+IQrlSnVE/bqGSyC2cz/9Le8=\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-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=\ngolang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8=\ngolang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I=\ngolang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=\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-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=\ngolang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=\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-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-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.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=\ngolang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=\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/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/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\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/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": "contrib/registry/consul/registry.go",
    "content": "package consul\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"sync\"\n\t\"sync/atomic\"\n\t\"time\"\n\n\t\"github.com/hashicorp/consul/api\"\n\n\t\"github.com/go-kratos/kratos/v2/registry\"\n)\n\nvar (\n\t_ registry.Registrar = (*Registry)(nil)\n\t_ registry.Discovery = (*Registry)(nil)\n)\n\n// Option is consul registry option.\ntype Option func(*Registry)\n\n// WithHealthCheck with registry health check option.\nfunc WithHealthCheck(enable bool) Option {\n\treturn func(o *Registry) {\n\t\to.enableHealthCheck = enable\n\t}\n}\n\n// WithTimeout with get services timeout option.\nfunc WithTimeout(timeout time.Duration) Option {\n\treturn func(o *Registry) {\n\t\to.timeout = timeout\n\t}\n}\n\n// WithDatacenter with registry datacenter option\nfunc WithDatacenter(dc Datacenter) Option {\n\treturn func(o *Registry) {\n\t\to.cli.dc = dc\n\t}\n}\n\n// WithHeartbeat enable or disable heartbeat\nfunc WithHeartbeat(enable bool) Option {\n\treturn func(o *Registry) {\n\t\tif o.cli != nil {\n\t\t\to.cli.heartbeat = enable\n\t\t}\n\t}\n}\n\n// WithServiceResolver with endpoint function option.\nfunc WithServiceResolver(fn ServiceResolver) Option {\n\treturn func(o *Registry) {\n\t\tif o.cli != nil {\n\t\t\to.cli.resolver = fn\n\t\t}\n\t}\n}\n\n// WithHealthCheckInterval with healthcheck interval in seconds.\nfunc WithHealthCheckInterval(interval int) Option {\n\treturn func(o *Registry) {\n\t\tif o.cli != nil {\n\t\t\to.cli.healthcheckInterval = interval\n\t\t}\n\t}\n}\n\n// WithDeregisterCriticalServiceAfter with deregister-critical-service-after in seconds.\nfunc WithDeregisterCriticalServiceAfter(interval int) Option {\n\treturn func(o *Registry) {\n\t\tif o.cli != nil {\n\t\t\to.cli.deregisterCriticalServiceAfter = interval\n\t\t}\n\t}\n}\n\n// WithServiceCheck with service checks\nfunc WithServiceCheck(checks ...*api.AgentServiceCheck) Option {\n\treturn func(o *Registry) {\n\t\tif o.cli != nil {\n\t\t\to.cli.serviceChecks = checks\n\t\t}\n\t}\n}\n\n// WithTags with service tags.\nfunc WithTags(tags []string) Option {\n\treturn func(o *Registry) {\n\t\tif o.cli != nil {\n\t\t\to.cli.tags = tags\n\t\t}\n\t}\n}\n\n// Config is consul registry config\ntype Config struct {\n\t*api.Config\n}\n\n// Registry is consul registry\ntype Registry struct {\n\tcli               *Client\n\tenableHealthCheck bool\n\tregistry          map[string]*serviceSet\n\tlock              sync.RWMutex\n\ttimeout           time.Duration\n}\n\n// New creates consul registry\nfunc New(apiClient *api.Client, opts ...Option) *Registry {\n\tr := &Registry{\n\t\tregistry:          make(map[string]*serviceSet),\n\t\tenableHealthCheck: true,\n\t\ttimeout:           10 * time.Second,\n\t\tcli: &Client{\n\t\t\tdc:                             SingleDatacenter,\n\t\t\tcli:                            apiClient,\n\t\t\tresolver:                       defaultResolver,\n\t\t\thealthcheckInterval:            10,\n\t\t\theartbeat:                      true,\n\t\t\tderegisterCriticalServiceAfter: 600,\n\t\t\tcancelers:                      make(map[string]*canceler),\n\t\t},\n\t}\n\tfor _, o := range opts {\n\t\to(r)\n\t}\n\treturn r\n}\n\n// Register register service\nfunc (r *Registry) Register(ctx context.Context, svc *registry.ServiceInstance) error {\n\treturn r.cli.Register(ctx, svc, r.enableHealthCheck)\n}\n\n// Deregister deregister service\nfunc (r *Registry) Deregister(ctx context.Context, svc *registry.ServiceInstance) error {\n\treturn r.cli.Deregister(ctx, svc.ID)\n}\n\n// GetService return service by name\nfunc (r *Registry) GetService(ctx context.Context, name string) ([]*registry.ServiceInstance, error) {\n\tr.lock.RLock()\n\tset := r.registry[name]\n\tr.lock.RUnlock()\n\n\tgetRemote := func() []*registry.ServiceInstance {\n\t\tservices, _, err := r.cli.Service(ctx, name, 0, true)\n\t\tif err == nil && len(services) > 0 {\n\t\t\treturn services\n\t\t}\n\t\treturn nil\n\t}\n\n\tif set == nil {\n\t\tif s := getRemote(); len(s) > 0 {\n\t\t\treturn s, nil\n\t\t}\n\t\treturn nil, fmt.Errorf(\"service %s not resolved in registry\", name)\n\t}\n\tss, _ := set.services.Load().([]*registry.ServiceInstance)\n\tif ss == nil {\n\t\tif s := getRemote(); len(s) > 0 {\n\t\t\treturn s, nil\n\t\t}\n\t\treturn nil, fmt.Errorf(\"service %s not found in registry\", name)\n\t}\n\treturn ss, nil\n}\n\n// ListServices return service list.\nfunc (r *Registry) ListServices() (allServices map[string][]*registry.ServiceInstance, err error) {\n\tr.lock.RLock()\n\tdefer r.lock.RUnlock()\n\tallServices = make(map[string][]*registry.ServiceInstance)\n\tfor name, set := range r.registry {\n\t\tvar services []*registry.ServiceInstance\n\t\tss, _ := set.services.Load().([]*registry.ServiceInstance)\n\t\tif ss == nil {\n\t\t\tcontinue\n\t\t}\n\t\tservices = append(services, ss...)\n\t\tallServices[name] = services\n\t}\n\treturn\n}\n\n// Watch resolve service by name\nfunc (r *Registry) Watch(ctx context.Context, name string) (registry.Watcher, error) {\n\tif err := ctx.Err(); err != nil {\n\t\treturn nil, err\n\t}\n\n\tr.lock.Lock()\n\tset, ok := r.registry[name]\n\tif !ok {\n\t\tcancelCtx, cancel := context.WithCancel(context.Background())\n\t\tset = &serviceSet{\n\t\t\tregistry:    r,\n\t\t\twatcher:     make(map[*watcher]struct{}),\n\t\t\tservices:    &atomic.Value{},\n\t\t\tserviceName: name,\n\t\t\tctx:         cancelCtx,\n\t\t\tcancel:      cancel,\n\t\t}\n\t\tr.registry[name] = set\n\t}\n\tset.ref.Add(1)\n\tr.lock.Unlock()\n\n\t// init watcher\n\tw := &watcher{\n\t\tevent: make(chan struct{}, 1),\n\t}\n\tw.ctx, w.cancel = context.WithCancel(ctx)\n\tw.set = set\n\tset.lock.Lock()\n\tset.watcher[w] = struct{}{}\n\tset.lock.Unlock()\n\n\tss, _ := set.services.Load().([]*registry.ServiceInstance)\n\tif len(ss) > 0 {\n\t\t// If the service has a value, it needs to be pushed to the watcher,\n\t\t// otherwise the initial data may be blocked forever during the watch.\n\t\tselect {\n\t\tcase w.event <- struct{}{}:\n\t\tdefault:\n\t\t}\n\t}\n\n\tif !ok {\n\t\tif err := r.resolve(ctx, set); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\treturn w, nil\n}\n\nfunc (r *Registry) resolve(ctx context.Context, ss *serviceSet) error {\n\tlistServices := r.cli.Service\n\tif r.timeout > 0 {\n\t\tlistServices = func(ctx context.Context, service string, index uint64, passingOnly bool) ([]*registry.ServiceInstance, uint64, error) {\n\t\t\ttimeoutCtx, cancel := context.WithTimeout(ctx, r.timeout)\n\t\t\tdefer cancel()\n\n\t\t\treturn r.cli.Service(timeoutCtx, service, index, passingOnly)\n\t\t}\n\t}\n\n\tservices, idx, err := listServices(ctx, ss.serviceName, 0, true)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif len(services) > 0 {\n\t\tss.broadcast(services)\n\t}\n\n\tgo func() {\n\t\tticker := time.NewTicker(time.Second)\n\t\tdefer ticker.Stop()\n\t\tfor {\n\t\t\tselect {\n\t\t\tcase <-ticker.C:\n\t\t\t\ttmpService, tmpIdx, err := listServices(ss.ctx, ss.serviceName, idx, true)\n\t\t\t\tif err != nil {\n\t\t\t\t\tif err := sleepCtx(ss.ctx, time.Second); err != nil {\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tif len(tmpService) != 0 && tmpIdx != idx {\n\t\t\t\t\tservices = tmpService\n\t\t\t\t\tss.broadcast(services)\n\t\t\t\t}\n\t\t\t\tidx = tmpIdx\n\t\t\tcase <-ss.ctx.Done():\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t}()\n\n\treturn nil\n}\n\nfunc (r *Registry) tryDelete(ss *serviceSet) bool {\n\tr.lock.Lock()\n\tdefer r.lock.Unlock()\n\tif ss.ref.Add(-1) != 0 {\n\t\treturn false\n\t}\n\tss.cancel()\n\tdelete(r.registry, ss.serviceName)\n\treturn true\n}\n"
  },
  {
    "path": "contrib/registry/consul/registry_test.go",
    "content": "package consul\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"net\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"reflect\"\n\t\"strconv\"\n\t\"sync\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/hashicorp/consul/api\"\n\n\t\"github.com/go-kratos/kratos/v2/registry\"\n)\n\nfunc tcpServer(lis net.Listener) {\n\tfor {\n\t\tconn, err := lis.Accept()\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t\tgo func() {\n\t\t\t_, _ = io.Copy(io.Discard, conn)\n\t\t\t_ = conn.Close()\n\t\t}()\n\t}\n}\n\nfunc TestRegistry_Register(t *testing.T) {\n\topts := []Option{\n\t\tWithHealthCheck(false),\n\t}\n\n\ttype args struct {\n\t\tctx        context.Context\n\t\tserverName string\n\t\tserver     []*registry.ServiceInstance\n\t}\n\n\ttest := []struct {\n\t\tname    string\n\t\targs    args\n\t\twant    []*registry.ServiceInstance\n\t\twantErr bool\n\t}{\n\t\t{\n\t\t\tname: \"normal\",\n\t\t\targs: args{\n\t\t\t\tctx:        context.Background(),\n\t\t\t\tserverName: \"server-1\",\n\t\t\t\tserver: []*registry.ServiceInstance{\n\t\t\t\t\t{\n\t\t\t\t\t\tID:        \"1\",\n\t\t\t\t\t\tName:      \"server-1\",\n\t\t\t\t\t\tVersion:   \"v0.0.1\",\n\t\t\t\t\t\tMetadata:  nil,\n\t\t\t\t\t\tEndpoints: []string{\"http://127.0.0.1:8000\"},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: []*registry.ServiceInstance{\n\t\t\t\t{\n\t\t\t\t\tID:        \"1\",\n\t\t\t\t\tName:      \"server-1\",\n\t\t\t\t\tVersion:   \"v0.0.1\",\n\t\t\t\t\tMetadata:  nil,\n\t\t\t\t\tEndpoints: []string{\"http://127.0.0.1:8000\"},\n\t\t\t\t},\n\t\t\t},\n\t\t\twantErr: false,\n\t\t},\n\t\t{\n\t\t\tname: \"registry new service replace old service\",\n\t\t\targs: args{\n\t\t\t\tctx:        context.Background(),\n\t\t\t\tserverName: \"server-1\",\n\t\t\t\tserver: []*registry.ServiceInstance{\n\t\t\t\t\t{\n\t\t\t\t\t\tID:        \"2\",\n\t\t\t\t\t\tName:      \"server-1\",\n\t\t\t\t\t\tVersion:   \"v0.0.1\",\n\t\t\t\t\t\tMetadata:  nil,\n\t\t\t\t\t\tEndpoints: []string{\"http://127.0.0.1:8000\"},\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tID:        \"2\",\n\t\t\t\t\t\tName:      \"server-1\",\n\t\t\t\t\t\tVersion:   \"v0.0.2\",\n\t\t\t\t\t\tMetadata:  nil,\n\t\t\t\t\t\tEndpoints: []string{\"http://127.0.0.1:8000\"},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: []*registry.ServiceInstance{\n\t\t\t\t{\n\t\t\t\t\tID:        \"2\",\n\t\t\t\t\tName:      \"server-1\",\n\t\t\t\t\tVersion:   \"v0.0.2\",\n\t\t\t\t\tMetadata:  nil,\n\t\t\t\t\tEndpoints: []string{\"http://127.0.0.1:8000\"},\n\t\t\t\t},\n\t\t\t},\n\t\t\twantErr: false,\n\t\t},\n\t}\n\n\tfor _, tt := range test {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tcli, err := api.NewClient(&api.Config{Address: \"127.0.0.1:8500\"})\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"create consul client failed: %v\", err)\n\t\t\t}\n\n\t\t\tr := New(cli, opts...)\n\n\t\t\tfor _, instance := range tt.args.server {\n\t\t\t\tinstance := instance\n\t\t\t\terr = r.Register(tt.args.ctx, instance)\n\t\t\t\tif err != nil {\n\t\t\t\t\tt.Error(err)\n\t\t\t\t}\n\t\t\t\tdefer func() {\n\t\t\t\t\terr = r.Deregister(tt.args.ctx, instance)\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\tt.Error(err)\n\t\t\t\t\t}\n\t\t\t\t}()\n\t\t\t}\n\t\t\twatchCtx, watchCancel := context.WithCancel(context.Background())\n\t\t\twatch, err := r.Watch(watchCtx, tt.args.serverName)\n\t\t\tif err != nil {\n\t\t\t\tt.Error(err)\n\t\t\t\twatchCancel()\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tgot, err := watch.Next()\n\t\t\tif (err != nil) != tt.wantErr {\n\t\t\t\tt.Errorf(\"GetService() error = %v, wantErr %v\", err, tt.wantErr)\n\t\t\t\tt.Errorf(\"GetService() got = %v\", got)\n\t\t\t\twatchCancel()\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif !reflect.DeepEqual(got, tt.want) {\n\t\t\t\tt.Errorf(\"GetService() got = %v, want %v\", got, tt.want)\n\t\t\t}\n\n\t\t\terr = watch.Stop()\n\t\t\tif err != nil {\n\t\t\t\tt.Error(err)\n\t\t\t}\n\t\t\twatchCancel()\n\t\t})\n\t}\n}\n\nfunc TestRegistry_GetService(t *testing.T) {\n\taddr := fmt.Sprintf(\"%s:9091\", getIntranetIP())\n\tlis, err := net.Listen(\"tcp\", addr)\n\tif err != nil {\n\t\tt.Errorf(\"listen tcp %s failed!\", addr)\n\t\tt.Fail()\n\t}\n\tdefer lis.Close()\n\tgo tcpServer(lis)\n\ttime.Sleep(time.Millisecond * 100)\n\tcli, err := api.NewClient(&api.Config{Address: \"127.0.0.1:8500\"})\n\tif err != nil {\n\t\tt.Fatalf(\"create consul client failed: %v\", err)\n\t}\n\topts := []Option{\n\t\tWithHeartbeat(true),\n\t\tWithHealthCheck(true),\n\t\tWithHealthCheckInterval(5),\n\t}\n\tr := New(cli, opts...)\n\n\tinstance1 := &registry.ServiceInstance{\n\t\tID:        \"1\",\n\t\tName:      \"server-1\",\n\t\tVersion:   \"v0.0.1\",\n\t\tEndpoints: []string{fmt.Sprintf(\"tcp://%s?isSecure=false\", addr)},\n\t}\n\n\tinstance2 := &registry.ServiceInstance{\n\t\tID:        \"2\",\n\t\tName:      \"server-1\",\n\t\tVersion:   \"v0.0.1\",\n\t\tEndpoints: []string{fmt.Sprintf(\"tcp://%s?isSecure=false\", addr)},\n\t}\n\n\ttype fields struct {\n\t\tregistry *Registry\n\t}\n\ttype args struct {\n\t\tctx         context.Context\n\t\tserviceName string\n\t}\n\ttests := []struct {\n\t\tname      string\n\t\tfields    fields\n\t\targs      args\n\t\twant      []*registry.ServiceInstance\n\t\twantErr   bool\n\t\tpreFunc   func(t *testing.T)\n\t\tdeferFunc func(t *testing.T)\n\t}{\n\t\t{\n\t\t\tname:   \"normal\",\n\t\t\tfields: fields{r},\n\t\t\targs: args{\n\t\t\t\tctx:         context.Background(),\n\t\t\t\tserviceName: \"server-1\",\n\t\t\t},\n\t\t\twant:    []*registry.ServiceInstance{instance1},\n\t\t\twantErr: false,\n\t\t\tpreFunc: func(t *testing.T) {\n\t\t\t\tif err := r.Register(context.Background(), instance1); err != nil {\n\t\t\t\t\tt.Error(err)\n\t\t\t\t}\n\t\t\t\twatchCtx, watchCancel := context.WithCancel(context.Background())\n\t\t\t\twatch, err := r.Watch(watchCtx, instance1.Name)\n\t\t\t\tif err != nil {\n\t\t\t\t\tt.Error(err)\n\t\t\t\t}\n\t\t\t\t_, err = watch.Next()\n\t\t\t\tif err != nil {\n\t\t\t\t\tt.Error(err)\n\t\t\t\t}\n\t\t\t\terr = watch.Stop()\n\t\t\t\tif err != nil {\n\t\t\t\t\tt.Error(err)\n\t\t\t\t}\n\t\t\t\twatchCancel()\n\t\t\t},\n\t\t\tdeferFunc: func(t *testing.T) {\n\t\t\t\terr := r.Deregister(context.Background(), instance1)\n\t\t\t\tif err != nil {\n\t\t\t\t\tt.Error(err)\n\t\t\t\t}\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:   \"can't get any\",\n\t\t\tfields: fields{r},\n\t\t\targs: args{\n\t\t\t\tctx:         context.Background(),\n\t\t\t\tserviceName: \"server-x\",\n\t\t\t},\n\t\t\twant:    nil,\n\t\t\twantErr: true,\n\t\t\tpreFunc: func(t *testing.T) {\n\t\t\t\tif err := r.Register(context.Background(), instance2); err != nil {\n\t\t\t\t\tt.Error(err)\n\t\t\t\t}\n\t\t\t\twatchCtx, watchCancel := context.WithCancel(context.Background())\n\t\t\t\twatch, err := r.Watch(watchCtx, instance2.Name)\n\t\t\t\tif err != nil {\n\t\t\t\t\tt.Error(err)\n\t\t\t\t}\n\t\t\t\t_, err = watch.Next()\n\t\t\t\tif err != nil {\n\t\t\t\t\tt.Error(err)\n\t\t\t\t}\n\t\t\t\terr = watch.Stop()\n\t\t\t\tif err != nil {\n\t\t\t\t\tt.Error(err)\n\t\t\t\t}\n\t\t\t\twatchCancel()\n\t\t\t},\n\t\t\tdeferFunc: func(t *testing.T) {\n\t\t\t\terr := r.Deregister(context.Background(), instance2)\n\t\t\t\tif err != nil {\n\t\t\t\t\tt.Error(err)\n\t\t\t\t}\n\t\t\t},\n\t\t},\n\t}\n\n\tfor _, test := range tests {\n\t\tt.Run(test.name, func(t *testing.T) {\n\t\t\tif test.preFunc != nil {\n\t\t\t\ttest.preFunc(t)\n\t\t\t}\n\t\t\tif test.deferFunc != nil {\n\t\t\t\tdefer test.deferFunc(t)\n\t\t\t}\n\n\t\t\tservice, err := test.fields.registry.GetService(context.Background(), test.args.serviceName)\n\t\t\tif (err != nil) != test.wantErr {\n\t\t\t\tt.Errorf(\"GetService() error = %v, wantErr %v\", err, test.wantErr)\n\t\t\t\tt.Errorf(\"GetService() got = %v\", service)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif !reflect.DeepEqual(service, test.want) {\n\t\t\t\tt.Errorf(\"GetService() got = %v, want %v\", service, test.want)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestRegistry_Watch(t *testing.T) {\n\taddr := fmt.Sprintf(\"%s:9091\", getIntranetIP())\n\tlis, err := net.Listen(\"tcp\", addr)\n\tif err != nil {\n\t\tt.Errorf(\"listen tcp %s failed!\", addr)\n\t\treturn\n\t}\n\tdefer lis.Close()\n\tgo tcpServer(lis)\n\n\tcli, err := api.NewClient(&api.Config{Address: \"127.0.0.1:8500\", WaitTime: 2 * time.Second})\n\tif err != nil {\n\t\tt.Fatalf(\"create consul client failed: %v\", err)\n\t}\n\n\tinstance1 := &registry.ServiceInstance{\n\t\tID:        \"1\",\n\t\tName:      \"server-1\",\n\t\tVersion:   \"v0.0.1\",\n\t\tEndpoints: []string{fmt.Sprintf(\"tcp://%s?isSecure=false\", addr)},\n\t}\n\n\tinstance2 := &registry.ServiceInstance{\n\t\tID:        \"2\",\n\t\tName:      \"server-1\",\n\t\tVersion:   \"v0.0.1\",\n\t\tEndpoints: []string{fmt.Sprintf(\"tcp://%s?isSecure=false\", addr)},\n\t}\n\n\tinstance3 := &registry.ServiceInstance{\n\t\tID:        \"3\",\n\t\tName:      \"server-1\",\n\t\tVersion:   \"v0.0.1\",\n\t\tEndpoints: []string{fmt.Sprintf(\"tcp://%s?isSecure=false\", addr)},\n\t}\n\n\ttype args struct {\n\t\tctx      context.Context\n\t\tcancel   func()\n\t\topts     []Option\n\t\tinstance *registry.ServiceInstance\n\t}\n\tcanceledCtx, cancel := context.WithCancel(context.Background())\n\n\ttests := []struct {\n\t\tname    string\n\t\targs    args\n\t\twant    []*registry.ServiceInstance\n\t\twantErr bool\n\t\tpreFunc func(t *testing.T)\n\t}{\n\t\t{\n\t\t\tname: \"normal\",\n\t\t\targs: args{\n\t\t\t\tctx:      context.Background(),\n\t\t\t\tinstance: instance1,\n\t\t\t\topts: []Option{\n\t\t\t\t\tWithHealthCheck(false),\n\t\t\t\t},\n\t\t\t},\n\t\t\twant:    []*registry.ServiceInstance{instance1},\n\t\t\twantErr: false,\n\t\t\tpreFunc: func(*testing.T) {},\n\t\t},\n\t\t{\n\t\t\tname: \"ctx has been canceled\",\n\t\t\targs: args{\n\t\t\t\tctx:      canceledCtx,\n\t\t\t\tcancel:   cancel,\n\t\t\t\tinstance: instance2,\n\t\t\t\topts: []Option{\n\t\t\t\t\tWithHealthCheck(false),\n\t\t\t\t},\n\t\t\t},\n\t\t\twant:    nil,\n\t\t\twantErr: true,\n\t\t\tpreFunc: func(*testing.T) {},\n\t\t},\n\t\t{\n\t\t\tname: \"register with healthCheck\",\n\t\t\targs: args{\n\t\t\t\tctx:      context.Background(),\n\t\t\t\tinstance: instance3,\n\t\t\t\topts: []Option{\n\t\t\t\t\tWithHeartbeat(true),\n\t\t\t\t\tWithHealthCheck(true),\n\t\t\t\t\tWithHealthCheckInterval(5),\n\t\t\t\t},\n\t\t\t},\n\t\t\twant:    []*registry.ServiceInstance{instance3},\n\t\t\twantErr: false,\n\t\t\tpreFunc: func(*testing.T) {},\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tif tt.preFunc != nil {\n\t\t\t\ttt.preFunc(t)\n\t\t\t}\n\n\t\t\tr := New(cli, tt.args.opts...)\n\n\t\t\terr := r.Register(tt.args.ctx, tt.args.instance)\n\t\t\tif err != nil {\n\t\t\t\tt.Error(err)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tdefer func() {\n\t\t\t\terr = r.Deregister(context.Background(), tt.args.instance)\n\t\t\t\tif err != nil {\n\t\t\t\t\tt.Error(err)\n\t\t\t\t}\n\t\t\t}()\n\t\t\twatch, err := r.Watch(tt.args.ctx, tt.args.instance.Name)\n\t\t\tif err != nil {\n\t\t\t\tt.Error(err)\n\t\t\t}\n\n\t\t\tif tt.args.cancel != nil {\n\t\t\t\ttt.args.cancel()\n\t\t\t}\n\n\t\t\tservice, err := watch.Next()\n\n\t\t\tif (err != nil) != tt.wantErr {\n\t\t\t\tt.Errorf(\"GetService() error = %v, wantErr %v\", err, tt.wantErr)\n\t\t\t\tt.Errorf(\"GetService() got = %v\", service)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif !reflect.DeepEqual(service, tt.want) {\n\t\t\t\tt.Errorf(\"GetService() got = %v, want %v\", service, tt.want)\n\t\t\t}\n\t\t\terr = watch.Stop()\n\t\t\tif err != nil {\n\t\t\t\tt.Error(err)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestRegistry_IdleAndWatch(t *testing.T) {\n\taddr := fmt.Sprintf(\"%s:9091\", getIntranetIP())\n\n\ttime.Sleep(time.Millisecond * 100)\n\tcli, err := api.NewClient(&api.Config{Address: \"127.0.0.1:8500\", WaitTime: 2 * time.Second})\n\tif err != nil {\n\t\tt.Fatalf(\"create consul client failed: %v\", err)\n\t}\n\n\tr := New(cli, []Option{\n\t\tWithHealthCheck(false),\n\t}...)\n\n\tinstance1 := &registry.ServiceInstance{\n\t\tID:        \"1\",\n\t\tName:      \"server-1\",\n\t\tVersion:   \"v0.0.1\",\n\t\tEndpoints: []string{fmt.Sprintf(\"tcp://%s?isSecure=false\", addr)},\n\t}\n\tinstance2 := &registry.ServiceInstance{\n\t\tID:        \"1\",\n\t\tName:      \"server-1\",\n\t\tVersion:   \"v0.0.2\",\n\t\tEndpoints: []string{fmt.Sprintf(\"tcp://%s?isSecure=false\", addr)},\n\t}\n\n\ttype args struct {\n\t\tctx            context.Context\n\t\tinstance       *registry.ServiceInstance\n\t\tchangeInstance *registry.ServiceInstance\n\t}\n\n\ttests := []struct {\n\t\tname  string\n\t\targs  args\n\t\twant1 []*registry.ServiceInstance\n\t\twant2 []*registry.ServiceInstance\n\t}{\n\t\t{\n\t\t\tname: \"many client, one idle\",\n\t\t\targs: args{\n\t\t\t\tctx:            context.Background(),\n\t\t\t\tinstance:       instance1,\n\t\t\t\tchangeInstance: instance2,\n\t\t\t},\n\t\t\twant1: []*registry.ServiceInstance{instance1},\n\t\t\twant2: []*registry.ServiceInstance{instance2},\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tvar watchs []registry.Watcher\n\t\t\tfor i := 0; i < 10; i++ {\n\t\t\t\twatch, err := r.Watch(tt.args.ctx, tt.args.instance.Name) //nolint\n\t\t\t\tif err != nil {\n\t\t\t\t\tt.Error(err)\n\t\t\t\t}\n\t\t\t\tdefer func() {\n\t\t\t\t\t_ = watch.Stop()\n\t\t\t\t}()\n\n\t\t\t\twatchs = append(watchs, watch)\n\t\t\t}\n\n\t\t\terr = r.Register(tt.args.ctx, tt.args.instance)\n\t\t\tif err != nil {\n\t\t\t\tt.Error(err)\n\t\t\t}\n\t\t\tdefer func() {\n\t\t\t\terr = r.Deregister(context.Background(), tt.args.instance)\n\t\t\t\tif err != nil {\n\t\t\t\t\tt.Error(err)\n\t\t\t\t}\n\t\t\t}()\n\n\t\t\tvar wg1 sync.WaitGroup\n\t\t\tfor _, watch := range watchs {\n\t\t\t\twg1.Add(1)\n\t\t\t\tgo func(watch registry.Watcher, want []*registry.ServiceInstance) {\n\t\t\t\t\tdefer wg1.Done()\n\n\t\t\t\t\t// first\n\t\t\t\t\tservice, err := watch.Next() //nolint\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\tt.Error(err)\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\t\t\t\t\tif !reflect.DeepEqual(service, want) {\n\t\t\t\t\t\tt.Errorf(\"GetService() got = %v, want = %v\", service, want)\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\t\t\t\t}(watch, tt.want1)\n\t\t\t}\n\t\t\twg1.Wait()\n\n\t\t\terr = r.Register(tt.args.ctx, tt.args.changeInstance)\n\t\t\tif err != nil {\n\t\t\t\tt.Error(err)\n\t\t\t}\n\t\t\tdefer func() {\n\t\t\t\terr := r.Deregister(context.Background(), tt.args.changeInstance)\n\t\t\t\tif err != nil {\n\t\t\t\t\tt.Error(err)\n\t\t\t\t}\n\t\t\t}()\n\n\t\t\tvar wg2 sync.WaitGroup\n\t\t\tfor _, watch := range watchs {\n\t\t\t\twg2.Add(1)\n\t\t\t\tgo func(watch registry.Watcher, want []*registry.ServiceInstance) {\n\t\t\t\t\tdefer wg2.Done()\n\n\t\t\t\t\t// instance changes\n\t\t\t\t\tservice, err := watch.Next() //nolint\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\tt.Error(err)\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\t\t\t\t\tif !reflect.DeepEqual(service, want) {\n\t\t\t\t\t\tt.Errorf(\"GetService() got = %v, want = %v\", service, want)\n\t\t\t\t\t}\n\t\t\t\t}(watch, tt.want2)\n\t\t\t}\n\t\t\twg2.Wait()\n\t\t})\n\t}\n}\n\nfunc TestRegistry_IdleAndWatch2(t *testing.T) {\n\taddr := fmt.Sprintf(\"%s:9091\", getIntranetIP())\n\n\ttime.Sleep(time.Millisecond * 100)\n\tcli, err := api.NewClient(&api.Config{Address: \"127.0.0.1:8500\", WaitTime: 2 * time.Second})\n\tif err != nil {\n\t\tt.Fatalf(\"create consul client failed: %v\", err)\n\t}\n\n\tinstance1 := &registry.ServiceInstance{\n\t\tID:        \"1\",\n\t\tName:      \"server-1\",\n\t\tVersion:   \"v0.0.1\",\n\t\tEndpoints: []string{fmt.Sprintf(\"tcp://%s?isSecure=false\", addr)},\n\t}\n\ttype args struct {\n\t\tctx      context.Context\n\t\topts     []Option\n\t\tinstance *registry.ServiceInstance\n\t}\n\n\ttests := []struct {\n\t\tname    string\n\t\targs    args\n\t\twant    []*registry.ServiceInstance\n\t\twantErr bool\n\t}{\n\t\t{\n\t\t\tname: \"all clients are idle, create a new one\",\n\t\t\targs: args{\n\t\t\t\tctx:      context.Background(),\n\t\t\t\tinstance: instance1,\n\t\t\t\topts: []Option{\n\t\t\t\t\tWithHealthCheck(false),\n\t\t\t\t},\n\t\t\t},\n\t\t\twant:    []*registry.ServiceInstance{instance1},\n\t\t\twantErr: false,\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tr := New(cli, tt.args.opts...)\n\n\t\t\terr = r.Register(tt.args.ctx, tt.args.instance)\n\t\t\tif err != nil {\n\t\t\t\tt.Error(err)\n\t\t\t}\n\t\t\tdefer func() {\n\t\t\t\terr = r.Deregister(tt.args.ctx, tt.args.instance)\n\t\t\t\tif err != nil {\n\t\t\t\t\tt.Error(err)\n\t\t\t\t}\n\t\t\t}()\n\n\t\t\tctx, cancel := context.WithCancel(context.Background())\n\t\t\tfor i := 0; i < 10; i++ {\n\t\t\t\tstopCtx, stopCancel := context.WithCancel(ctx)\n\t\t\t\twatch, err1 := r.Watch(stopCtx, tt.args.instance.Name)\n\t\t\t\tif err1 != nil {\n\t\t\t\t\tt.Error(err1)\n\t\t\t\t}\n\t\t\t\tgo func(_ int) {\n\t\t\t\t\t// first\n\t\t\t\t\tservice, err2 := watch.Next()\n\t\t\t\t\tif (err2 != nil) != tt.wantErr {\n\t\t\t\t\t\tt.Errorf(\"GetService() error = %v, wantErr %v\", err, tt.wantErr)\n\t\t\t\t\t\tt.Errorf(\"GetService() got = %v\", service)\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\t\t\t\t}(i)\n\t\t\t\tgo func() {\n\t\t\t\t\tselect {\n\t\t\t\t\tcase <-stopCtx.Done():\n\t\t\t\t\t\terr1 = watch.Stop()\n\t\t\t\t\t\tif err1 != nil {\n\t\t\t\t\t\t\tt.Errorf(\"watch stop err:%v\", err)\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn\n\t\t\t\t\tcase <-time.After(time.Minute):\n\t\t\t\t\t\tstopCancel()\n\t\t\t\t\t\terr1 = watch.Stop()\n\t\t\t\t\t\tif err1 != nil {\n\t\t\t\t\t\t\tt.Errorf(\"watch stop err:%v\", err)\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\t\t\t\t}()\n\t\t\t}\n\t\t\ttime.Sleep(time.Second * 3)\n\t\t\tcancel()\n\t\t\ttime.Sleep(time.Second * 2)\n\t\t\t// Everything is idle. Add new watch.\n\t\t\twatchCtx, watchCancel := context.WithCancel(context.Background())\n\t\t\twatch, err := r.Watch(watchCtx, tt.args.instance.Name)\n\t\t\tif err != nil {\n\t\t\t\tt.Error(err)\n\t\t\t}\n\t\t\tservice, err := watch.Next()\n\t\t\tif (err != nil) != tt.wantErr {\n\t\t\t\tt.Errorf(\"GetService() error = %v, wantErr %v\", err, tt.wantErr)\n\t\t\t\tt.Errorf(\"GetService() got = %v\", service)\n\t\t\t\twatchCancel()\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif !reflect.DeepEqual(service, tt.want) {\n\t\t\t\tt.Errorf(\"GetService() got = %v, want %v\", service, tt.want)\n\t\t\t}\n\t\t\twatchCancel()\n\t\t})\n\t}\n}\n\nfunc TestRegistry_ExitOldResolverAndReWatch(t *testing.T) {\n\taddr := fmt.Sprintf(\"%s:9091\", getIntranetIP())\n\n\ttime.Sleep(time.Millisecond * 100)\n\tcli, err := api.NewClient(&api.Config{Address: \"127.0.0.1:8500\", WaitTime: 2 * time.Second})\n\tif err != nil {\n\t\tt.Fatalf(\"create consul client failed: %v\", err)\n\t}\n\n\tinstance1 := &registry.ServiceInstance{\n\t\tID:        \"1\",\n\t\tName:      \"server-1\",\n\t\tVersion:   \"v0.0.1\",\n\t\tEndpoints: []string{fmt.Sprintf(\"tcp://%s?isSecure=false\", addr)},\n\t}\n\tinstance2 := &registry.ServiceInstance{\n\t\tID:        \"2\",\n\t\tName:      \"server-1\",\n\t\tVersion:   \"v0.0.2\",\n\t\tEndpoints: []string{fmt.Sprintf(\"tcp://%s?isSecure=false\", addr)},\n\t}\n\ttype args struct {\n\t\tctx             context.Context\n\t\topts            []Option\n\t\tinstance        *registry.ServiceInstance\n\t\tinitialInstance *registry.ServiceInstance\n\t}\n\n\ttests := []struct {\n\t\tname    string\n\t\targs    args\n\t\twant    []*registry.ServiceInstance\n\t\twantErr bool\n\t}{\n\t\t{\n\t\t\tname: \"When it has entered idle mode, but the old resolver has not completely exited, the watch will be re-established due to new requests coming in.\",\n\t\t\targs: args{\n\t\t\t\tctx:             context.Background(),\n\t\t\t\tinitialInstance: instance1,\n\t\t\t\tinstance:        instance2,\n\t\t\t\topts: []Option{\n\t\t\t\t\tWithHealthCheck(false),\n\t\t\t\t\tWithTimeout(time.Second * 2),\n\t\t\t\t},\n\t\t\t},\n\t\t\twant:    []*registry.ServiceInstance{instance2},\n\t\t\twantErr: false,\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tr := New(cli, tt.args.opts...)\n\n\t\t\terr = r.Register(tt.args.ctx, tt.args.initialInstance)\n\t\t\tif err != nil {\n\t\t\t\tt.Error(err)\n\t\t\t}\n\t\t\t// first watch\n\t\t\tctx, cancel := context.WithCancel(context.Background())\n\t\t\twatch, err := r.Watch(ctx, tt.args.instance.Name)\n\t\t\tif err != nil {\n\t\t\t\tt.Error(err)\n\t\t\t}\n\t\t\tservice, err := watch.Next()\n\t\t\tif (err != nil) != tt.wantErr {\n\t\t\t\tt.Errorf(\"GetService() error = %v, wantErr %v\", err, tt.wantErr)\n\t\t\t\tt.Errorf(\"GetService() got = %v\", service)\n\t\t\t}\n\n\t\t\ttime.Sleep(time.Second * 3)\n\t\t\t// The simulation entered idle mode first, but the old resolver was not closed yet, and new requests triggered a new Watch.\n\t\t\twatchCtx := context.Background()\n\t\t\t// old resolver cancel\n\t\t\terr = watch.Stop()\n\t\t\tif err != nil {\n\t\t\t\tt.Errorf(\"watch stop err:%v\", err)\n\t\t\t}\n\t\t\tcancel()\n\t\t\t// If it sleeps for a period of time, the old resolve goroutine will exit before the new Watch is processed, and there will be no problems at this time.\n\t\t\t// time.Sleep(time.Second * 8)\n\t\t\tnewWatch, err := r.Watch(watchCtx, tt.args.instance.Name)\n\t\t\tif err != nil {\n\t\t\t\tt.Error(err)\n\t\t\t}\n\t\t\tservice, err = newWatch.Next()\n\t\t\tif (err != nil) != tt.wantErr {\n\t\t\t\tt.Errorf(\"GetService() error = %v, wantErr %v\", err, tt.wantErr)\n\t\t\t\tt.Errorf(\"GetService() got = %v\", service)\n\t\t\t}\n\t\t\t// change register info\n\t\t\ttime.Sleep(time.Second * 1)\n\t\t\terr = r.Deregister(tt.args.ctx, tt.args.initialInstance)\n\t\t\tif err != nil {\n\t\t\t\tt.Error(err)\n\t\t\t}\n\t\t\ttime.Sleep(time.Second * 5)\n\t\t\terr = r.Register(tt.args.ctx, tt.args.instance)\n\t\t\tif err != nil {\n\t\t\t\tt.Error(err)\n\t\t\t}\n\t\t\tdefer func() {\n\t\t\t\terr = r.Deregister(tt.args.ctx, tt.args.instance)\n\t\t\t\tif err != nil {\n\t\t\t\t\tt.Error(err)\n\t\t\t\t}\n\t\t\t}()\n\n\t\t\ttime.Sleep(time.Second * 2)\n\n\t\t\tnewWatchCtx, newWatchCancel := context.WithCancel(context.Background())\n\t\t\tc := make(chan struct{}, 1)\n\n\t\t\tgo func() {\n\t\t\t\tservice, err = newWatch.Next()\n\t\t\t\tif (err != nil) != tt.wantErr {\n\t\t\t\t\tt.Errorf(\"GetService() error = %v, wantErr %v\", err, tt.wantErr)\n\t\t\t\t\tt.Errorf(\"GetService() got = %v\", service)\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tif !reflect.DeepEqual(service, tt.want) {\n\t\t\t\t\tt.Errorf(\"GetService() got = %v, want %v\", service, tt.want)\n\t\t\t\t}\n\t\t\t\tc <- struct{}{}\n\t\t\t}()\n\t\t\ttime.AfterFunc(time.Second*10, newWatchCancel)\n\t\t\tselect {\n\t\t\tcase <-newWatchCtx.Done():\n\t\t\t\tt.Errorf(\"Timeout getservice. May be no new resolve goroutine to obtain the latest service information\")\n\t\t\tcase <-c:\n\t\t\t\treturn\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestRegistry_ShareServiceSet(t *testing.T) {\n\tlastIndex := uint64(0)\n\tserviceName := \"share-service-set\"\n\tmux := http.NewServeMux()\n\tmux.HandleFunc(\"/v1/health/service/\"+serviceName, func(w http.ResponseWriter, r *http.Request) {\n\t\tvar index uint64\n\t\tif s := r.URL.Query().Get(\"index\"); s != \"\" {\n\t\t\tval, err := strconv.ParseUint(s, 10, 64)\n\t\t\tif err != nil {\n\t\t\t\thttp.Error(w, err.Error(), http.StatusBadRequest)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tindex = val\n\t\t}\n\n\t\tif index < lastIndex {\n\t\t\tmsg := \"repeated request, not the same ServiceSet\"\n\t\t\thttp.Error(w, msg, http.StatusBadRequest)\n\t\t\tt.Error(msg)\n\t\t\tt.FailNow()\n\t\t\treturn\n\t\t}\n\n\t\tlastIndex = index + 1\n\t\tw.Header().Set(\"X-Consul-Index\", strconv.FormatUint(lastIndex, 10))\n\n\t\tout := []*api.ServiceEntry{\n\t\t\t{\n\t\t\t\tService: &api.AgentService{\n\t\t\t\t\tID:      \"1\",\n\t\t\t\t\tService: serviceName,\n\t\t\t\t},\n\t\t\t},\n\t\t}\n\t\terr := json.NewEncoder(w).Encode(out)\n\t\tif err != nil {\n\t\t\thttp.Error(w, err.Error(), http.StatusInternalServerError)\n\t\t}\n\t})\n\n\tts := httptest.NewServer(mux)\n\tdefer ts.Close()\n\n\tcli, err := api.NewClient(&api.Config{Address: ts.URL, WaitTime: 2 * time.Second})\n\tif err != nil {\n\t\tt.Fatalf(\"create consul client failed: %v\", err)\n\t}\n\n\tvar prev registry.Watcher\n\tr := New(cli, WithHealthCheck(false), WithHeartbeat(false))\n\tfor i := 0; i < 100; i++ {\n\t\tw, err := r.Watch(context.Background(), serviceName) //nolint\n\t\tif err != nil {\n\t\t\tt.Error(err)\n\t\t\treturn\n\t\t}\n\t\t// close previous watcher\n\t\tif prev != nil {\n\t\t\tif err = prev.Stop(); err != nil {\n\t\t\t\tt.Error(err)\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t\tprev = w\n\t}\n\n\ttime.Sleep(time.Second * 5)\n\n\tif prev != nil {\n\t\tif err = prev.Stop(); err != nil {\n\t\t\tt.Error(err)\n\t\t\treturn\n\t\t}\n\t}\n}\n\nfunc TestRegistry_MultiWatch(t *testing.T) {\n\tcli, err := api.NewClient(&api.Config{Address: \"127.0.0.1:8500\", WaitTime: 2 * time.Second})\n\tif err != nil {\n\t\tt.Fatalf(\"create consul client failed: %v\", err)\n\t}\n\n\tserviceName := \"multi-watch\"\n\taddr := fmt.Sprintf(\"%s:9091\", getIntranetIP())\n\tinstances := []*registry.ServiceInstance{\n\t\t{\n\t\t\tID:        \"1\",\n\t\t\tName:      serviceName,\n\t\t\tVersion:   \"v1.0.0\",\n\t\t\tEndpoints: []string{fmt.Sprintf(\"tcp://%s?isSecure=false\", addr)},\n\t\t},\n\t\t{\n\t\t\tID:        \"2\",\n\t\t\tName:      serviceName,\n\t\t\tVersion:   \"v1.0.0\",\n\t\t\tEndpoints: []string{fmt.Sprintf(\"tcp://%s?isSecure=false\", addr)},\n\t\t},\n\t}\n\n\tr := New(cli, WithHealthCheck(false), WithHeartbeat(true))\n\terr = r.Register(context.Background(), instances[0])\n\tif err != nil {\n\t\tt.Error(err)\n\t\treturn\n\t}\n\tdefer func() {\n\t\terr = r.Deregister(context.Background(), instances[0])\n\t\tif err != nil {\n\t\t\tt.Error(err)\n\t\t}\n\t}()\n\n\twatch1, err := r.Watch(context.Background(), serviceName)\n\tif err != nil {\n\t\tt.Error(err)\n\t\treturn\n\t}\n\tdefer func() {\n\t\tif err = watch1.Stop(); err != nil {\n\t\t\tt.Error(err)\n\t\t}\n\t}()\n\n\twatch2, err := r.Watch(context.Background(), serviceName)\n\tif err != nil {\n\t\tt.Error(err)\n\t\treturn\n\t}\n\tdefer func() {\n\t\tif err = watch2.Stop(); err != nil {\n\t\t\tt.Error(err)\n\t\t}\n\t}()\n\n\tgot1, err := watch1.Next()\n\tif err != nil {\n\t\tt.Error(err)\n\t\treturn\n\t}\n\n\tgot2, err := watch2.Next()\n\tif err != nil {\n\t\tt.Error(err)\n\t\treturn\n\t}\n\n\tif !reflect.DeepEqual(got1, instances[:1]) {\n\t\tt.Errorf(\"got = %v, want = %v\", got1, instances[:1])\n\t\treturn\n\t}\n\tif !reflect.DeepEqual(got2, instances[:1]) {\n\t\tt.Errorf(\"got = %v, want = %v\", got2, instances[:1])\n\t\treturn\n\t}\n\n\t// close first watcher\n\tif err = watch1.Stop(); err != nil {\n\t\tt.Error(err)\n\t\treturn\n\t}\n\n\t// register a new instance\n\terr = r.Register(context.Background(), instances[1])\n\tif err != nil {\n\t\tt.Error(err)\n\t\treturn\n\t}\n\tdefer func() {\n\t\terr = r.Deregister(context.Background(), instances[1])\n\t\tif err != nil {\n\t\t\tt.Error(err)\n\t\t}\n\t}()\n\n\t// second watcher should get the new instance\n\tgot, err := watch2.Next()\n\tif err != nil {\n\t\tt.Error(err)\n\t\treturn\n\t}\n\n\tif !reflect.DeepEqual(got, instances[:2]) {\n\t\tt.Errorf(\"got = %v, want = %v\", got, instances[:2])\n\t\treturn\n\t}\n}\n\nfunc getIntranetIP() string {\n\taddrs, err := net.InterfaceAddrs()\n\tif err != nil {\n\t\treturn \"127.0.0.1\"\n\t}\n\n\tfor _, address := range addrs {\n\t\tif ipnet, ok := address.(*net.IPNet); ok && !ipnet.IP.IsLoopback() {\n\t\t\tif ipnet.IP.To4() != nil {\n\t\t\t\treturn ipnet.IP.String()\n\t\t\t}\n\t\t}\n\t}\n\treturn \"127.0.0.1\"\n}\n"
  },
  {
    "path": "contrib/registry/consul/service.go",
    "content": "package consul\n\nimport (\n\t\"context\"\n\t\"sync\"\n\t\"sync/atomic\"\n\n\t\"github.com/go-kratos/kratos/v2/registry\"\n)\n\ntype serviceSet struct {\n\tregistry    *Registry\n\tserviceName string\n\twatcher     map[*watcher]struct{}\n\tref         atomic.Int32\n\tservices    *atomic.Value\n\tlock        sync.RWMutex\n\n\t// for cancel\n\tctx    context.Context\n\tcancel context.CancelFunc\n}\n\nfunc (s *serviceSet) broadcast(ss []*registry.ServiceInstance) {\n\ts.services.Store(ss)\n\ts.lock.RLock()\n\tdefer s.lock.RUnlock()\n\tfor k := range s.watcher {\n\t\tselect {\n\t\tcase k.event <- struct{}{}:\n\t\tdefault:\n\t\t}\n\t}\n}\n\nfunc (s *serviceSet) delete(w *watcher) {\n\ts.lock.Lock()\n\tdelete(s.watcher, w)\n\ts.lock.Unlock()\n\ts.registry.tryDelete(s)\n}\n"
  },
  {
    "path": "contrib/registry/consul/watcher.go",
    "content": "package consul\n\nimport (\n\t\"context\"\n\n\t\"github.com/go-kratos/kratos/v2/registry\"\n)\n\ntype watcher struct {\n\tevent chan struct{}\n\tset   *serviceSet\n\n\t// for cancel\n\tctx    context.Context\n\tcancel context.CancelFunc\n}\n\nfunc (w *watcher) Next() (services []*registry.ServiceInstance, err error) {\n\tif err = w.ctx.Err(); err != nil {\n\t\treturn\n\t}\n\n\tselect {\n\tcase <-w.ctx.Done():\n\t\terr = w.ctx.Err()\n\t\treturn\n\tcase <-w.event:\n\t}\n\n\tss, ok := w.set.services.Load().([]*registry.ServiceInstance)\n\tif ok {\n\t\tservices = append(services, ss...)\n\t}\n\treturn\n}\n\nfunc (w *watcher) Stop() error {\n\tif w.cancel != nil {\n\t\tw.cancel()\n\t\tw.cancel = nil\n\t\tw.set.delete(w)\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "contrib/registry/discovery/README.md",
    "content": "## Discovery Registry\n\nThis module implements a `registry.Registrar` and `registry.Discovery` interface in kratos based `bilibili/discovery`.\n\n[![go.dev reference](https://img.shields.io/badge/go.dev-reference-007d9c?logo=go&logoColor=white&style=flat-square)](https://pkg.go.dev/github.com/go-kratos/kratos/contrib/registry/discovery/v2)\n\n### Quick Start\n\n**_Register a service_**\n\n```go\nimport (\n\t\"github.com/go-kratos/kratos/contrib/registry/discovery/v2\"\n)\n\nfunc main() {\n\t// initialize a registry\n\tr := discovery.New(&discovery.Config{\n\t\tNodes:  []string{\"0.0.0.0:7171\"},\n\t\tEnv:    \"dev\",\n\t\tRegion: \"sh1\",\n\t\tZone:   \"zone1\",\n\t\tHost:   \"hostname\",\n\t})\n\n\t// construct srv instance\n\t// ...\n\n\tapp := kratos.New(\n\t\tkratos.Name(\"helloworld\"),\n\t\tkratos.Server(\n\t\t\thttpSrv,\n\t\t\tgrpcSrv,\n\t\t),\n\t\tkratos.Metadata(map[string]string{\"color\": \"gray\"}),\n\t\t// use Registrar\n\t\tkratos.Registrar(r),\n\t)\n\n\tif err := app.Run(); err != nil {\n\t\tlog.NewHelper(logger).Fatal(err)\n\t}\n}\n```\n\n**_Discover a service_**\n\n```go\nimport (\n\t\"github.com/go-kratos/kratos/contrib/registry/discovery/v2\"\n\t\"github.com/go-kratos/kratos/v2/transport/grpc\"\n)\n\nfunc main() {\n\t// initialize a discovery\n\tr := discovery.New(&discovery.Config{\n\t\tNodes:  []string{\"0.0.0.0:7171\"},\n\t\tEnv:    \"dev\",\n\t\tRegion: \"sh1\",\n\t\tZone:   \"zone1\",\n\t\tHost:   \"localhost\",\n\t}, nil)\n\n\tconn, err := grpc.DialInsecure(\n\t\tcontext.Background(),\n\t\tgrpc.WithEndpoint(\"discovery:///appid\"),\n\t\t// use discovery\n\t\tgrpc.WithDiscovery(r),\n\t)\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\tdefer conn.Close()\n\n\t// request and log\n}\n```\n\n### Config explain\n\n```go\ntype Config struct {\n\tNodes  []string // discovery nodes address\n\tRegion string   // region of the service, sh\n\tZone   string   // zone of region, sh001\n\tEnv    string   // env of service, dev, prod and etc\n\tHost   string   // hostname of service\n}\n```\n\n### References\n\n- [bilibili/discovery](https://github.com/bilibili/discovery)\n"
  },
  {
    "path": "contrib/registry/discovery/discovery.go",
    "content": "package discovery\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"math/rand/v2\"\n\t\"net/url\"\n\t\"strconv\"\n\t\"sync\"\n\t\"sync/atomic\"\n\t\"time\"\n\n\t\"github.com/go-resty/resty/v2\"\n\t\"github.com/pkg/errors\"\n\n\t\"github.com/go-kratos/kratos/v2/log\"\n)\n\ntype Discovery struct {\n\tconfig     *Config\n\tonce       sync.Once\n\tctx        context.Context\n\tcancelFunc context.CancelFunc\n\thttpClient *resty.Client\n\n\tnode    atomic.Value\n\tnodeIdx atomic.Uint64\n\n\tmutex       sync.RWMutex\n\tapps        map[string]*appInfo\n\tregistry    map[string]struct{}\n\tlastHost    string\n\tcancelPolls context.CancelFunc\n}\n\ntype appInfo struct {\n\tresolver map[*Resolve]struct{}\n\tzoneIns  atomic.Value\n\tlastTs   int64 // latest timestamp\n}\n\n// New construct a Discovery instance which implements registry.Registrar,\n// registry.Discovery and registry.Watcher.\nfunc New(c *Config) *Discovery {\n\tif c == nil {\n\t\tc = new(Config)\n\t}\n\tif err := fixConfig(c); err != nil {\n\t\tpanic(err)\n\t}\n\tctx, cancel := context.WithCancel(context.Background())\n\td := &Discovery{\n\t\tconfig:     c,\n\t\tctx:        ctx,\n\t\tcancelFunc: cancel,\n\t\tapps:       map[string]*appInfo{},\n\t\tregistry:   map[string]struct{}{},\n\t}\n\n\td.httpClient = resty.New().\n\t\tSetTimeout(40 * time.Second)\n\n\t// Discovery self found and watch\n\tr := d.resolveBuild(_discoveryAppID)\n\tevent := r.Watch()\n\t_, ok := <-event\n\tif !ok {\n\t\tpanic(\"Discovery watch self failed\")\n\t}\n\tdiscoveryIns, ok := r.fetch(context.Background())\n\tif ok {\n\t\td.newSelf(discoveryIns.Instances)\n\t}\n\tgo d.selfProc(r, event)\n\n\treturn d\n}\n\n// Close stop all running process including Discovery and register\nfunc (d *Discovery) Close() error {\n\td.cancelFunc()\n\treturn nil\n}\n\n// selfProc start a goroutine to refresh Discovery self registration information.\nfunc (d *Discovery) selfProc(resolver *Resolve, event <-chan struct{}) {\n\tfor {\n\t\t_, ok := <-event\n\t\tif !ok {\n\t\t\treturn\n\t\t}\n\t\tzones, ok := resolver.fetch(context.Background())\n\t\tif ok {\n\t\t\td.newSelf(zones.Instances)\n\t\t}\n\t}\n}\n\n// newSelf\nfunc (d *Discovery) newSelf(zones map[string][]*discoveryInstance) {\n\tins, ok := zones[d.config.Zone]\n\tif !ok {\n\t\treturn\n\t}\n\tvar nodes []string\n\tfor _, in := range ins {\n\t\tfor _, addr := range in.Addrs {\n\t\t\tu, err := url.Parse(addr)\n\t\t\tif err == nil && u.Scheme == \"http\" {\n\t\t\t\tnodes = append(nodes, u.Host)\n\t\t\t}\n\t\t}\n\t}\n\t// diff old nodes\n\tvar olds int\n\tif node, ok := d.node.Load().([]string); ok {\n\t\tfor _, n := range nodes {\n\t\t\tfor _, o := range node {\n\t\t\t\tif o == n {\n\t\t\t\t\tolds++\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\tif len(nodes) == olds {\n\t\treturn\n\t}\n\n\trand.Shuffle(len(nodes), func(i, j int) {\n\t\tnodes[i], nodes[j] = nodes[j], nodes[i]\n\t})\n\td.node.Store(nodes)\n}\n\n// resolveBuild Discovery resolver builder.\nfunc (d *Discovery) resolveBuild(appID string) *Resolve {\n\tr := &Resolve{\n\t\tid:    appID,\n\t\td:     d,\n\t\tevent: make(chan struct{}, 1),\n\t}\n\n\td.mutex.Lock()\n\tapp, ok := d.apps[appID]\n\tif !ok {\n\t\tapp = &appInfo{\n\t\t\tresolver: make(map[*Resolve]struct{}),\n\t\t}\n\t\td.apps[appID] = app\n\t\tcancel := d.cancelPolls\n\t\tif cancel != nil {\n\t\t\tcancel()\n\t\t}\n\t}\n\tapp.resolver[r] = struct{}{}\n\td.mutex.Unlock()\n\tif ok {\n\t\tselect {\n\t\tcase r.event <- struct{}{}:\n\t\tdefault:\n\t\t}\n\t}\n\n\tlog.Debugf(\"Discovery: AddWatch(%s) already watch(%v)\", appID, ok)\n\td.once.Do(func() {\n\t\tgo d.serverProc()\n\t})\n\treturn r\n}\n\nfunc (d *Discovery) serverProc() {\n\tdefer log.Debug(\"Discovery serverProc quit\")\n\n\tvar (\n\t\tctx    context.Context\n\t\tcancel context.CancelFunc\n\t)\n\n\tticker := time.NewTicker(time.Minute * 30)\n\tdefer ticker.Stop()\n\n\tfor {\n\t\tif ctx == nil {\n\t\t\tctx, cancel = context.WithCancel(d.ctx)\n\t\t\td.mutex.Lock()\n\t\t\td.cancelPolls = cancel\n\t\t\td.mutex.Unlock()\n\t\t}\n\t\tselect {\n\t\tcase <-d.ctx.Done():\n\t\t\treturn\n\t\tcase <-ticker.C:\n\t\t\td.switchNode()\n\t\tdefault:\n\t\t}\n\n\t\tapps, err := d.polls(ctx)\n\t\tif err != nil {\n\t\t\td.switchNode()\n\t\t\tif errors.Is(ctx.Err(), context.Canceled) {\n\t\t\t\tctx = nil\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\ttime.Sleep(time.Second)\n\t\t\tcontinue\n\t\t}\n\t\td.broadcast(apps)\n\t}\n}\n\nfunc (d *Discovery) pickNode() string {\n\tnodes, ok := d.node.Load().([]string)\n\tif !ok || len(nodes) == 0 {\n\t\treturn d.config.Nodes[rand.IntN(len(d.config.Nodes))]\n\t}\n\treturn nodes[d.nodeIdx.Load()%uint64(len(nodes))]\n}\n\nfunc (d *Discovery) switchNode() {\n\td.nodeIdx.Add(1)\n}\n\n// renew an instance with Discovery\nfunc (d *Discovery) renew(ctx context.Context, ins *discoveryInstance) (err error) {\n\td.mutex.RLock()\n\tc := d.config\n\td.mutex.RUnlock()\n\n\tres := new(discoveryCommonResp)\n\turi := fmt.Sprintf(_renewURL, d.pickNode())\n\n\t// construct parameters to renew\n\tp := newParams(d.config)\n\tp.Set(_paramKeyAppID, ins.AppID)\n\n\t// send request to Discovery server.\n\tif _, err = d.httpClient.R().\n\t\tSetContext(ctx).\n\t\tSetQueryParamsFromValues(p).\n\t\tSetResult(&res).\n\t\tPost(uri); err != nil {\n\t\td.switchNode()\n\t\tlog.Errorf(\"Discovery: renew client.Get(%v) env(%s) appid(%s) hostname(%s) error(%v)\",\n\t\t\turi, c.Env, ins.AppID, c.Host, err)\n\t\treturn\n\t}\n\n\tif res.Code != _codeOK {\n\t\terr = fmt.Errorf(\"discovery.renew failed ErrorCode: %d\", res.Code)\n\t\tif res.Code == _codeNotFound {\n\t\t\tif err = d.register(ctx, ins); err != nil {\n\t\t\t\terr = errors.Wrap(err, \"Discovery.renew instance, and failed to register ins\")\n\t\t\t}\n\t\t\treturn\n\t\t}\n\n\t\tlog.Errorf(\n\t\t\t\"Discovery: renew client.Get(%v) env(%s) appid(%s) hostname(%s) code(%v)\",\n\t\t\turi, c.Env, ins.AppID, c.Host, res.Code,\n\t\t)\n\t}\n\n\treturn\n}\n\n// cancel Remove the registered instance from Discovery\nfunc (d *Discovery) cancel(ins *discoveryInstance) (err error) {\n\td.mutex.RLock()\n\tconfig := d.config\n\td.mutex.RUnlock()\n\n\tres := new(discoveryCommonResp)\n\turi := fmt.Sprintf(_cancelURL, d.pickNode())\n\tp := newParams(d.config)\n\tp.Set(_paramKeyAppID, ins.AppID)\n\n\t// request\n\t// send request to Discovery server.\n\tif _, err = d.httpClient.R().\n\t\tSetContext(context.Background()).\n\t\tSetQueryParamsFromValues(p).\n\t\tSetResult(&res).\n\t\tPost(uri); err != nil {\n\t\td.switchNode()\n\t\tlog.Errorf(\"Discovery cancel client.Get(%v) env(%s) appid(%s) hostname(%s) error(%v)\",\n\t\t\turi, config.Env, ins.AppID, config.Host, err)\n\t\treturn\n\t}\n\n\t// handle response error\n\tif res.Code != _codeOK {\n\t\tif res.Code == _codeNotFound {\n\t\t\treturn nil\n\t\t}\n\n\t\tlog.Warnf(\"Discovery cancel client.Get(%v) env(%s) appid(%s) hostname(%s) code(%v)\",\n\t\t\turi, config.Env, ins.AppID, config.Host, res.Code)\n\t\terr = fmt.Errorf(\"ErrorCode: %d\", res.Code)\n\t\treturn\n\t}\n\n\treturn\n}\n\nfunc (d *Discovery) broadcast(apps map[string]*disInstancesInfo) {\n\tfor appID, v := range apps {\n\t\tvar count int\n\t\t// v maybe nil in old version(less than v1.1) Discovery, check in case of panic\n\t\tif v == nil {\n\t\t\tcontinue\n\t\t}\n\t\tfor zone, ins := range v.Instances {\n\t\t\tif len(ins) == 0 {\n\t\t\t\tdelete(v.Instances, zone)\n\t\t\t}\n\t\t\tcount += len(ins)\n\t\t}\n\t\tif count == 0 {\n\t\t\tcontinue\n\t\t}\n\t\td.mutex.RLock()\n\t\tapp, ok := d.apps[appID]\n\t\td.mutex.RUnlock()\n\t\tif ok {\n\t\t\tapp.lastTs = v.LastTs\n\t\t\tapp.zoneIns.Store(v)\n\t\t\td.mutex.RLock()\n\t\t\tfor rs := range app.resolver {\n\t\t\t\tselect {\n\t\t\t\tcase rs.event <- struct{}{}:\n\t\t\t\tdefault:\n\t\t\t\t}\n\t\t\t}\n\t\t\td.mutex.RUnlock()\n\t\t}\n\t}\n}\n\nfunc (d *Discovery) polls(ctx context.Context) (apps map[string]*disInstancesInfo, err error) {\n\tvar (\n\t\tlastTss = make([]int64, 0, 4)\n\t\tappIDs  = make([]string, 0, 16)\n\t\thost    = d.pickNode()\n\t\tchanged bool\n\t)\n\tif host != d.lastHost {\n\t\td.lastHost = host\n\t\tchanged = true\n\t}\n\n\td.mutex.RLock()\n\tconfig := d.config\n\tfor k, v := range d.apps {\n\t\tif changed {\n\t\t\tv.lastTs = 0\n\t\t}\n\t\tappIDs = append(appIDs, k)\n\t\tlastTss = append(lastTss, v.lastTs)\n\t}\n\td.mutex.RUnlock()\n\n\t// if there is no app, polls just return.\n\tif len(appIDs) == 0 {\n\t\treturn\n\t}\n\n\turi := fmt.Sprintf(_pollURL, host)\n\tres := new(discoveryPollsResp)\n\n\t// params\n\tp := newParams(nil)\n\tp.Set(_paramKeyEnv, config.Env)\n\tp.Set(_paramKeyHostname, config.Host)\n\tfor _, appID := range appIDs {\n\t\tp.Add(_paramKeyAppID, appID)\n\t}\n\tfor _, ts := range lastTss {\n\t\tp.Add(\"latest_timestamp\", strconv.FormatInt(ts, 10))\n\t}\n\n\t// request\n\treqURI := uri + \"?\" + p.Encode()\n\tif _, err = d.httpClient.R().\n\t\tSetContext(ctx).\n\t\tSetQueryParamsFromValues(p).\n\t\tSetResult(res).Get(uri); err != nil {\n\t\td.switchNode()\n\t\tlog.Errorf(\"Discovery: client.Get(%s) error(%+v)\", reqURI, err)\n\t\treturn nil, err\n\t}\n\n\tif res.Code != _codeOK {\n\t\tif res.Code != _codeNotModified {\n\t\t\tlog.Errorf(\"Discovery: client.Get(%s) get error code(%d)\", reqURI, res.Code)\n\t\t}\n\t\terr = fmt.Errorf(\"discovery.polls failed ErrCode: %d\", res.Code)\n\t\treturn\n\t}\n\n\tfor _, app := range res.Data {\n\t\tif app.LastTs == 0 {\n\t\t\terr = ErrServerError\n\t\t\tlog.Errorf(\"Discovery: client.Get(%s) latest_timestamp is 0, instances:(%+v)\", reqURI, res.Data)\n\t\t\treturn\n\t\t}\n\t}\n\n\tlog.Debugf(\"Discovery: successfully polls(%s) instances (%+v)\", reqURI, res.Data)\n\tapps = res.Data\n\treturn\n}\n\n// Resolve Discovery resolver.\ntype Resolve struct {\n\tid    string\n\tevent chan struct{}\n\td     *Discovery\n}\n\n// Watch instance.\nfunc (r *Resolve) Watch() <-chan struct{} {\n\treturn r.event\n}\n\n// fetch resolver instance.\nfunc (r *Resolve) fetch(_ context.Context) (ins *disInstancesInfo, ok bool) {\n\tr.d.mutex.RLock()\n\tapp, ok := r.d.apps[r.id]\n\tr.d.mutex.RUnlock()\n\tif ok {\n\t\tvar appIns *disInstancesInfo\n\t\tappIns, ok = app.zoneIns.Load().(*disInstancesInfo)\n\t\tif !ok {\n\t\t\treturn\n\t\t}\n\t\tins = new(disInstancesInfo)\n\t\tins.LastTs = appIns.LastTs\n\t\tins.Scheduler = appIns.Scheduler\n\t\tins.Instances = make(map[string][]*discoveryInstance, len(appIns.Instances))\n\t\tfor zone, in := range appIns.Instances {\n\t\t\tins.Instances[zone] = in\n\t\t}\n\t}\n\treturn\n}\n\n// Close resolver\nfunc (r *Resolve) Close() error {\n\tr.d.mutex.Lock()\n\tif app, ok := r.d.apps[r.id]; ok && len(app.resolver) != 0 {\n\t\tdelete(app.resolver, r)\n\t\t// TODO: delete app from builder\n\t}\n\tr.d.mutex.Unlock()\n\treturn nil\n}\n"
  },
  {
    "path": "contrib/registry/discovery/discovery_helper.go",
    "content": "package discovery\n\nimport (\n\t\"fmt\"\n\t\"net/url\"\n\t\"os\"\n\t\"strconv\"\n\t\"time\"\n\n\t\"github.com/pkg/errors\"\n\n\t\"github.com/go-kratos/kratos/v2/registry\"\n)\n\nvar (\n\tErrDuplication = errors.New(\"register failed: instance duplicated: \")\n\tErrServerError = errors.New(\"server error\")\n)\n\nconst (\n\t// Discovery server resource uri\n\t_registerURL = \"http://%s/discovery/register\"\n\t//_setURL      = \"http://%s/discovery/set\"\n\t_cancelURL = \"http://%s/discovery/cancel\"\n\t_renewURL  = \"http://%s/discovery/renew\"\n\t_pollURL   = \"http://%s/discovery/polls\"\n\n\t// Discovery server error codes\n\t_codeOK          = 0\n\t_codeNotFound    = -404\n\t_codeNotModified = -304\n\t//_SERVER_ERROR = -500\n\n\t// _registerGap is the gap to renew instance registration.\n\t_registerGap    = 30 * time.Second\n\t_statusUP       = \"1\"\n\t_discoveryAppID = \"infra.discovery\"\n)\n\n// Config Discovery configures.\ntype Config struct {\n\tNodes  []string\n\tRegion string\n\tZone   string\n\tEnv    string\n\tHost   string\n}\n\nfunc fixConfig(c *Config) error {\n\tif c.Host == \"\" {\n\t\tc.Host, _ = os.Hostname()\n\t}\n\tif len(c.Nodes) == 0 || c.Region == \"\" || c.Zone == \"\" || c.Env == \"\" || c.Host == \"\" {\n\t\treturn fmt.Errorf(\n\t\t\t\"invalid Discovery config nodes:%+v region:%s zone:%s deployEnv:%s host:%s\",\n\t\t\tc.Nodes,\n\t\t\tc.Region,\n\t\t\tc.Zone,\n\t\t\tc.Env,\n\t\t\tc.Host,\n\t\t)\n\t}\n\treturn nil\n}\n\n// discoveryInstance represents a server the client connects to.\ntype discoveryInstance struct {\n\tRegion   string   `json:\"region\"`           // Region is region.\n\tZone     string   `json:\"zone\"`             // Zone is IDC.\n\tEnv      string   `json:\"env\"`              // Env prod/pre/uat/fat1\n\tAppID    string   `json:\"appid\"`            // AppID is mapping service-tree appId.\n\tHostname string   `json:\"hostname\"`         // Hostname is hostname from docker\n\tAddrs    []string `json:\"addrs\"`            // Addrs is the address of app instance format: scheme://host\n\tVersion  string   `json:\"version\"`          // Version is publishing version.\n\tLastTs   int64    `json:\"latest_timestamp\"` // LastTs is instance latest updated timestamp\n\t// Metadata is the information associated with Addr, which may be used to make load balancing decision.\n\tMetadata map[string]string `json:\"metadata\"`\n\tStatus   int64             `json:\"status\"` // Status instance status, eg: 1UP 2Waiting\n}\n\nconst _reservedInstanceIDKey = \"kratos.v2.serviceinstance.id\"\n\n// fromServerInstance convert registry.ServiceInstance into discoveryInstance\nfunc fromServerInstance(ins *registry.ServiceInstance, config *Config) *discoveryInstance {\n\tif ins == nil {\n\t\treturn nil\n\t}\n\n\tmetadata := ins.Metadata\n\tif ins.Metadata == nil {\n\t\tmetadata = make(map[string]string, 8)\n\t}\n\tmetadata[_reservedInstanceIDKey] = ins.ID\n\n\treturn &discoveryInstance{\n\t\tRegion:   config.Region,\n\t\tZone:     config.Zone,\n\t\tEnv:      config.Env,\n\t\tAppID:    ins.Name,\n\t\tHostname: config.Host,\n\t\tAddrs:    ins.Endpoints,\n\t\tVersion:  ins.Version,\n\t\tLastTs:   time.Now().Unix(),\n\t\tMetadata: metadata,\n\t\tStatus:   1,\n\t}\n}\n\n// toServiceInstance convert discoveryInstance into registry.ServiceInstance\nfunc toServiceInstance(ins *discoveryInstance) *registry.ServiceInstance {\n\tif ins == nil {\n\t\treturn nil\n\t}\n\n\tmd := map[string]string{\n\t\t\"region\":   ins.Region,\n\t\t\"zone\":     ins.Zone,\n\t\t\"lastTs\":   strconv.FormatInt(ins.LastTs, 10),\n\t\t\"env\":      ins.Env,\n\t\t\"hostname\": ins.Hostname,\n\t}\n\n\tif len(ins.Metadata) != 0 {\n\t\tfor k, v := range ins.Metadata {\n\t\t\tmd[k] = v\n\t\t}\n\t}\n\n\treturn &registry.ServiceInstance{\n\t\tID:        ins.Metadata[_reservedInstanceIDKey],\n\t\tName:      ins.AppID,\n\t\tVersion:   ins.Version,\n\t\tMetadata:  md,\n\t\tEndpoints: ins.Addrs,\n\t}\n}\n\n// disInstancesInfo instance info.\ntype disInstancesInfo struct {\n\tInstances map[string][]*discoveryInstance `json:\"instances\"`\n\tLastTs    int64                           `json:\"latest_timestamp\"`\n\tScheduler *scheduler                      `json:\"scheduler\"`\n}\n\n// scheduler scheduler.\ntype scheduler struct {\n\tClients map[string]*zoneStrategy `json:\"clients\"`\n}\n\n// zoneStrategy is the scheduling strategy of all zones\ntype zoneStrategy struct {\n\tZones map[string]*strategy `json:\"zones\"`\n}\n\n// strategy is zone scheduling strategy.\ntype strategy struct {\n\tWeight int64 `json:\"weight\"`\n}\n\nconst (\n\t_paramKeyRegion   = \"region\"\n\t_paramKeyZone     = \"zone\"\n\t_paramKeyEnv      = \"env\"\n\t_paramKeyHostname = \"hostname\"\n\t_paramKeyAppID    = \"appid\"\n\t_paramKeyAddrs    = \"addrs\"\n\t_paramKeyVersion  = \"version\"\n\t_paramKeyStatus   = \"status\"\n\t_paramKeyMetadata = \"metadata\"\n)\n\nfunc newParams(c *Config) url.Values {\n\tp := make(url.Values, 8)\n\tif c == nil {\n\t\treturn p\n\t}\n\n\tp.Set(_paramKeyRegion, c.Region)\n\tp.Set(_paramKeyZone, c.Zone)\n\tp.Set(_paramKeyEnv, c.Env)\n\tp.Set(_paramKeyHostname, c.Host)\n\treturn p\n}\n\ntype discoveryCommonResp struct {\n\tCode    int    `json:\"code\"`\n\tMessage string `json:\"message\"`\n}\n\ntype discoveryPollsResp struct {\n\tCode int                          `json:\"code\"`\n\tData map[string]*disInstancesInfo `json:\"data\"`\n}\n"
  },
  {
    "path": "contrib/registry/discovery/go.mod",
    "content": "module github.com/go-kratos/kratos/contrib/registry/discovery/v2\n\ngo 1.22\n\nrequire (\n\tgithub.com/go-kratos/kratos/v2 v2.9.2\n\tgithub.com/go-resty/resty/v2 v2.11.0\n\tgithub.com/pkg/errors v0.9.1\n)\n\nrequire golang.org/x/net v0.33.0 // indirect\n\nreplace github.com/go-kratos/kratos/v2 => ../../../\n"
  },
  {
    "path": "contrib/registry/discovery/go.sum",
    "content": "github.com/go-resty/resty/v2 v2.11.0 h1:i7jMfNOJYMp69lq7qozJP+bjgzfAzeOhuGlyDrqxT/8=\ngithub.com/go-resty/resty/v2 v2.11.0/go.mod h1:iiP/OpA0CkcL3IGt1O0+/SIItFUbkkyw5BGXiVdTu+A=\ngithub.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=\ngithub.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=\ngithub.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=\ngolang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=\ngolang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=\ngolang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=\ngolang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=\ngolang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=\ngolang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=\ngolang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=\ngolang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=\ngolang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=\ngolang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=\ngolang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I=\ngolang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=\ngolang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=\ngolang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=\ngolang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=\ngolang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=\ngolang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=\ngolang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=\ngolang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U=\ngolang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\ngolang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=\ngolang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=\ngolang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=\ngolang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=\ngolang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4=\ngolang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=\ngolang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=\ngolang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=\ngolang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\n"
  },
  {
    "path": "contrib/registry/discovery/impl_discover.go",
    "content": "package discovery\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"time\"\n\n\t\"github.com/pkg/errors\"\n\n\t\"github.com/go-kratos/kratos/v2/registry\"\n)\n\nfunc filterInstancesByZone(ins *disInstancesInfo, zone string) []*registry.ServiceInstance {\n\tzoneInstance, ok := ins.Instances[zone]\n\tif !ok || len(zoneInstance) == 0 {\n\t\treturn nil\n\t}\n\n\tout := make([]*registry.ServiceInstance, 0, len(zoneInstance))\n\tfor _, v := range zoneInstance {\n\t\tif v == nil {\n\t\t\tcontinue\n\t\t}\n\t\tout = append(out, toServiceInstance(v))\n\t}\n\n\treturn out\n}\n\nfunc (d *Discovery) GetService(ctx context.Context, serviceName string) ([]*registry.ServiceInstance, error) {\n\tr := d.resolveBuild(serviceName)\n\tins, ok := r.fetch(ctx)\n\tif !ok {\n\t\treturn nil, errors.New(\"Discovery.GetService fetch failed\")\n\t}\n\n\tout := filterInstancesByZone(ins, d.config.Zone)\n\tif len(out) == 0 {\n\t\treturn nil, fmt.Errorf(\"Discovery.GetService(%s) not found\", serviceName)\n\t}\n\n\treturn out, nil\n}\n\nfunc (d *Discovery) Watch(ctx context.Context, serviceName string) (registry.Watcher, error) {\n\treturn &watcher{\n\t\tresolve:     d.resolveBuild(serviceName),\n\t\tserviceName: serviceName,\n\t\tcancelCtx:   ctx,\n\t}, nil\n}\n\ntype watcher struct {\n\tresolve *Resolve\n\n\tcancelCtx   context.Context\n\tserviceName string\n}\n\nfunc (w *watcher) Next() ([]*registry.ServiceInstance, error) {\n\tevent := w.resolve.Watch()\n\n\tselect {\n\tcase <-event:\n\t// change event come\n\tcase <-w.cancelCtx.Done():\n\t\treturn nil, fmt.Errorf(\"watch context canceled: %v\", w.cancelCtx.Err())\n\t}\n\n\tctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)\n\tdefer cancel()\n\n\tins, ok := w.resolve.fetch(ctx)\n\tif !ok {\n\t\treturn nil, errors.New(\"Discovery.GetService fetch failed\")\n\t}\n\n\tout := filterInstancesByZone(ins, w.resolve.d.config.Zone)\n\tif len(out) == 0 {\n\t\treturn nil, fmt.Errorf(\"Discovery.GetService(%s) not found\", w.serviceName)\n\t}\n\n\treturn out, nil\n}\n\nfunc (w *watcher) Stop() error {\n\treturn w.resolve.Close()\n}\n"
  },
  {
    "path": "contrib/registry/discovery/impl_registrar.go",
    "content": "package discovery\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"strconv\"\n\t\"time\"\n\n\t\"github.com/pkg/errors\"\n\n\t\"github.com/go-kratos/kratos/v2/log\"\n\t\"github.com/go-kratos/kratos/v2/registry\"\n)\n\nfunc (d *Discovery) Register(ctx context.Context, service *registry.ServiceInstance) (err error) {\n\tins := fromServerInstance(service, d.config)\n\n\td.mutex.Lock()\n\tif _, ok := d.registry[ins.AppID]; ok {\n\t\terr = errors.Wrap(ErrDuplication, ins.AppID)\n\t} else {\n\t\td.registry[ins.AppID] = struct{}{}\n\t}\n\td.mutex.Unlock()\n\tif err != nil {\n\t\treturn\n\t}\n\n\tctx, cancel := context.WithCancel(d.ctx)\n\tif err = d.register(ctx, ins); err != nil {\n\t\td.mutex.Lock()\n\t\tdelete(d.registry, ins.AppID)\n\t\td.mutex.Unlock()\n\t\tcancel()\n\t\treturn\n\t}\n\n\tch := make(chan struct{}, 1)\n\td.cancelFunc = func() {\n\t\tcancel()\n\t\t<-ch\n\t}\n\n\t// renew the current register_service\n\tgo func() {\n\t\tdefer log.Warn(\"Discovery:register_service goroutine quit\")\n\t\tticker := time.NewTicker(_registerGap)\n\t\tdefer ticker.Stop()\n\t\tfor {\n\t\t\tselect {\n\t\t\tcase <-ticker.C:\n\t\t\t\t_ = d.renew(ctx, ins)\n\t\t\tcase <-ctx.Done():\n\t\t\t\t_ = d.cancel(ins)\n\t\t\t\tch <- struct{}{}\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t}()\n\n\treturn\n}\n\n// register an instance with Discovery\nfunc (d *Discovery) register(ctx context.Context, ins *discoveryInstance) (err error) {\n\td.mutex.RLock()\n\tc := d.config\n\td.mutex.RUnlock()\n\n\tvar metadata []byte\n\tif ins.Metadata != nil {\n\t\tif metadata, err = json.Marshal(ins.Metadata); err != nil {\n\t\t\tlog.Errorf(\n\t\t\t\t\"Discovery:register instance Marshal metadata(%v) failed!error(%v)\", ins.Metadata, err,\n\t\t\t)\n\t\t}\n\t}\n\tres := new(struct {\n\t\tCode    int    `json:\"code\"`\n\t\tMessage string `json:\"message\"`\n\t})\n\turi := fmt.Sprintf(_registerURL, d.pickNode())\n\n\t// params\n\tp := newParams(d.config)\n\tp.Set(_paramKeyAppID, ins.AppID)\n\tfor _, addr := range ins.Addrs {\n\t\tp.Add(_paramKeyAddrs, addr)\n\t}\n\tp.Set(_paramKeyVersion, ins.Version)\n\tif ins.Status == 0 {\n\t\tp.Set(_paramKeyStatus, _statusUP)\n\t} else {\n\t\tp.Set(_paramKeyStatus, strconv.FormatInt(ins.Status, 10))\n\t}\n\tp.Set(_paramKeyMetadata, string(metadata))\n\n\t// send request to Discovery server.\n\tif _, err = d.httpClient.R().\n\t\tSetContext(ctx).\n\t\tSetQueryParamsFromValues(p).\n\t\tSetResult(&res).\n\t\tPost(uri); err != nil {\n\t\td.switchNode()\n\t\tlog.Errorf(\"Discovery: register client.Get(%s) zone(%s) env(%s) appid(%s) addrs(%v) error(%v)\",\n\t\t\turi+\"?\"+p.Encode(), c.Zone, c.Env, ins.AppID, ins.Addrs, err)\n\t\treturn\n\t}\n\n\tif res.Code != 0 {\n\t\terr = fmt.Errorf(\"ErrorCode: %d\", res.Code)\n\t\tlog.Errorf(\"Discovery: register client.Get(%v) env(%s) appid(%s) addrs(%v) code(%v)\",\n\t\t\turi, c.Env, ins.AppID, ins.Addrs, res.Code)\n\t}\n\n\tlog.Infof(\n\t\t\"Discovery: register client.Get(%v) env(%s) appid(%s) addrs(%s) success\\n\",\n\t\turi, c.Env, ins.AppID, ins.Addrs,\n\t)\n\n\treturn\n}\n\nfunc (d *Discovery) Deregister(_ context.Context, service *registry.ServiceInstance) error {\n\tins := fromServerInstance(service, d.config)\n\treturn d.cancel(ins)\n}\n"
  },
  {
    "path": "contrib/registry/etcd/go.mod",
    "content": "module github.com/go-kratos/kratos/contrib/registry/etcd/v2\n\ngo 1.22\n\nrequire (\n\tgithub.com/go-kratos/kratos/v2 v2.9.2\n\tgo.etcd.io/etcd/client/v3 v3.5.11\n\tgoogle.golang.org/grpc v1.61.1\n)\n\nrequire (\n\tgithub.com/coreos/go-semver v0.3.0 // indirect\n\tgithub.com/coreos/go-systemd/v22 v22.3.2 // indirect\n\tgithub.com/gogo/protobuf v1.3.2 // indirect\n\tgithub.com/golang/protobuf v1.5.4 // indirect\n\tgo.etcd.io/etcd/api/v3 v3.5.11 // indirect\n\tgo.etcd.io/etcd/client/pkg/v3 v3.5.11 // indirect\n\tgo.uber.org/atomic v1.7.0 // indirect\n\tgo.uber.org/multierr v1.6.0 // indirect\n\tgo.uber.org/zap v1.17.0 // indirect\n\tgolang.org/x/net v0.33.0 // indirect\n\tgolang.org/x/sys v0.28.0 // indirect\n\tgolang.org/x/text v0.21.0 // indirect\n\tgoogle.golang.org/genproto v0.0.0-20231212172506-995d672761c0 // indirect\n\tgoogle.golang.org/genproto/googleapis/api v0.0.0-20240102182953-50ed04b92917 // indirect\n\tgoogle.golang.org/genproto/googleapis/rpc v0.0.0-20240102182953-50ed04b92917 // indirect\n\tgoogle.golang.org/protobuf v1.33.0 // indirect\n)\n\nreplace github.com/go-kratos/kratos/v2 => ../../../\n"
  },
  {
    "path": "contrib/registry/etcd/go.sum",
    "content": "github.com/coreos/go-semver v0.3.0 h1:wkHLiw0WNATZnSG7epLsujiMCgPAc9xhjJ4tgnAxmfM=\ngithub.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=\ngithub.com/coreos/go-systemd/v22 v22.3.2 h1:D9/bQk5vlXQFZ6Kwuu6zaiXJ9oTPe68++AzAJc1DzSI=\ngithub.com/coreos/go-systemd/v22 v22.3.2/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 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=\ngithub.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=\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.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=\ngithub.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=\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/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/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=\ngithub.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=\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/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\ngithub.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=\ngithub.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngithub.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=\ngithub.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=\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.5.11 h1:B54KwXbWDHyD3XYAwprxNzTe7vlhR69LuBgZnMVvS7E=\ngo.etcd.io/etcd/api/v3 v3.5.11/go.mod h1:Ot+o0SWSyT6uHhA56al1oCED0JImsRiU9Dc26+C2a+4=\ngo.etcd.io/etcd/client/pkg/v3 v3.5.11 h1:bT2xVspdiCj2910T0V+/KHcVKjkUrCZVtk8J2JF2z1A=\ngo.etcd.io/etcd/client/pkg/v3 v3.5.11/go.mod h1:seTzl2d9APP8R5Y2hFL3NVlD6qC/dOT+3kvrqPyTas4=\ngo.etcd.io/etcd/client/v3 v3.5.11 h1:ajWtgoNSZJ1gmS8k+icvPtqsqEav+iUorF7b0qozgUU=\ngo.etcd.io/etcd/client/v3 v3.5.11/go.mod h1:a6xQUEqFJ8vztO1agJh/KQKOMfFI8og52ZconzcDJwE=\ngo.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw=\ngo.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=\ngo.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4=\ngo.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=\ngo.uber.org/zap v1.17.0 h1:MTjgFu6ZLKvY6Pvaqk97GlxNBuMpV4Hy/3P6tRGlI2U=\ngo.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo=\ngolang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=\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/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-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190620200207-3b0461eec859/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.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I=\ngolang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=\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/sys v0.0.0-20190215142949-d0b11bdaac8a/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-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=\ngolang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=\ngolang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\ngolang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=\ngolang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=\ngolang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\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=\ngoogle.golang.org/genproto v0.0.0-20231212172506-995d672761c0 h1:YJ5pD9rF8o9Qtta0Cmy9rdBwkSjrTCT6XTiUQVOtIos=\ngoogle.golang.org/genproto v0.0.0-20231212172506-995d672761c0/go.mod h1:l/k7rMz0vFTBPy+tFSGvXEd3z+BcoG1k7EHbqm+YBsY=\ngoogle.golang.org/genproto/googleapis/api v0.0.0-20240102182953-50ed04b92917 h1:rcS6EyEaoCO52hQDupoSfrxI3R6C2Tq741is7X8OvnM=\ngoogle.golang.org/genproto/googleapis/api v0.0.0-20240102182953-50ed04b92917/go.mod h1:CmlNWB9lSezaYELKS5Ym1r44VrrbPUa7JTvw+6MbpJ0=\ngoogle.golang.org/genproto/googleapis/rpc v0.0.0-20240102182953-50ed04b92917 h1:6G8oQ016D88m1xAKljMlBOOGWDZkes4kMhgGFlf8WcQ=\ngoogle.golang.org/genproto/googleapis/rpc v0.0.0-20240102182953-50ed04b92917/go.mod h1:xtjpI3tXFPP051KaWnhvxkiubL/6dJ18vLVf7q2pTOU=\ngoogle.golang.org/grpc v1.61.1 h1:kLAiWrZs7YeDM6MumDe7m3y4aM6wacLzM1Y/wiLP9XY=\ngoogle.golang.org/grpc v1.61.1/go.mod h1:VUbo7IFqmF1QtCAstipjG0GIoq49KvMe9+h1jFLBNJs=\ngoogle.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=\ngoogle.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=\ngopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=\ngopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=\ngopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\ngopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\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": "contrib/registry/etcd/registry.go",
    "content": "package etcd\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"math/rand/v2\"\n\t\"time\"\n\n\tclientv3 \"go.etcd.io/etcd/client/v3\"\n\n\t\"github.com/go-kratos/kratos/v2/registry\"\n)\n\nvar (\n\t_ registry.Registrar = (*Registry)(nil)\n\t_ registry.Discovery = (*Registry)(nil)\n)\n\n// Option is etcd registry option.\ntype Option func(o *options)\n\ntype options struct {\n\tctx       context.Context\n\tnamespace string\n\tttl       time.Duration\n\tmaxRetry  int\n}\n\n// Context with registry context.\nfunc Context(ctx context.Context) Option {\n\treturn func(o *options) { o.ctx = ctx }\n}\n\n// Namespace with registry namespace.\nfunc Namespace(ns string) Option {\n\treturn func(o *options) { o.namespace = ns }\n}\n\n// RegisterTTL with register ttl.\nfunc RegisterTTL(ttl time.Duration) Option {\n\treturn func(o *options) { o.ttl = ttl }\n}\n\nfunc MaxRetry(num int) Option {\n\treturn func(o *options) { o.maxRetry = num }\n}\n\n// Registry is etcd registry.\ntype Registry struct {\n\topts   *options\n\tclient *clientv3.Client\n\tkv     clientv3.KV\n\tlease  clientv3.Lease\n\t/*\n\t\tctxMap is used to store the context cancel function of each service instance.\n\t\tWhen the service instance is deregistered, the corresponding context cancel function is called to stop the heartbeat.\n\t*/\n\tctxMap map[string]*serviceCancel\n}\n\ntype serviceCancel struct {\n\tservice *registry.ServiceInstance\n\tcancel  context.CancelFunc\n}\n\n// New creates etcd registry\nfunc New(client *clientv3.Client, opts ...Option) (r *Registry) {\n\top := &options{\n\t\tctx:       context.Background(),\n\t\tnamespace: \"/microservices\",\n\t\tttl:       time.Second * 15,\n\t\tmaxRetry:  5,\n\t}\n\tfor _, o := range opts {\n\t\to(op)\n\t}\n\treturn &Registry{\n\t\topts:   op,\n\t\tclient: client,\n\t\tkv:     clientv3.NewKV(client),\n\t\tctxMap: make(map[string]*serviceCancel),\n\t}\n}\n\n// Register the registration.\nfunc (r *Registry) Register(ctx context.Context, service *registry.ServiceInstance) error {\n\tkey := r.registerKey(service)\n\tvalue, err := marshal(service)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif r.lease != nil {\n\t\tr.lease.Close()\n\t}\n\tr.lease = clientv3.NewLease(r.client)\n\tleaseID, err := r.registerWithKV(ctx, key, value)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\thctx, cancel := context.WithCancel(r.opts.ctx)\n\tr.ctxMap[key] = &serviceCancel{\n\t\tservice: service,\n\t\tcancel:  cancel,\n\t}\n\tgo r.heartBeat(hctx, leaseID, key, value)\n\treturn nil\n}\n\nfunc (r *Registry) registerKey(service *registry.ServiceInstance) string {\n\treturn fmt.Sprintf(\"%s/%s/%s\", r.opts.namespace, service.Name, service.ID)\n}\n\n// Deregister the registration.\nfunc (r *Registry) Deregister(ctx context.Context, service *registry.ServiceInstance) error {\n\tdefer func() {\n\t\tif r.lease != nil {\n\t\t\tr.lease.Close()\n\t\t}\n\t}()\n\t// cancel heartbeat\n\tkey := r.registerKey(service)\n\tif serviceCancel, ok := r.ctxMap[key]; ok {\n\t\tserviceCancel.cancel()\n\t\tdelete(r.ctxMap, key)\n\t}\n\t_, err := r.client.Delete(ctx, key)\n\treturn err\n}\n\n// GetService return the service instances in memory according to the service name.\nfunc (r *Registry) GetService(ctx context.Context, name string) ([]*registry.ServiceInstance, error) {\n\tkey := r.serviceKey(name)\n\tresp, err := r.kv.Get(ctx, key, clientv3.WithPrefix())\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\titems := make([]*registry.ServiceInstance, 0, len(resp.Kvs))\n\tfor _, kv := range resp.Kvs {\n\t\tsi, err := unmarshal(kv.Value)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tif si.Name != name {\n\t\t\tcontinue\n\t\t}\n\t\titems = append(items, si)\n\t}\n\treturn items, nil\n}\n\nfunc (r *Registry) serviceKey(name string) string {\n\treturn fmt.Sprintf(\"%s/%s\", r.opts.namespace, name)\n}\n\n// Watch creates a watcher according to the service name.\nfunc (r *Registry) Watch(ctx context.Context, name string) (registry.Watcher, error) {\n\tkey := r.serviceKey(name)\n\treturn newWatcher(ctx, key, name, r.client)\n}\n\n// registerWithKV create a new lease, return current leaseID\nfunc (r *Registry) registerWithKV(ctx context.Context, key string, value string) (clientv3.LeaseID, error) {\n\tgrant, err := r.lease.Grant(ctx, int64(r.opts.ttl.Seconds()))\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\t_, err = r.client.Put(ctx, key, value, clientv3.WithLease(grant.ID))\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\treturn grant.ID, nil\n}\n\nfunc (r *Registry) heartBeat(ctx context.Context, leaseID clientv3.LeaseID, key string, value string) {\n\tcurLeaseID := leaseID\n\tkac, err := r.client.KeepAlive(ctx, leaseID)\n\tif err != nil {\n\t\tcurLeaseID = 0\n\t}\n\trandSource := rand.New(rand.NewPCG(uint64(time.Now().UnixNano()), 0))\n\n\tfor {\n\t\tif curLeaseID == 0 {\n\t\t\t// try to registerWithKV\n\t\t\tvar retreat []int\n\t\t\tfor retryCnt := 0; retryCnt < r.opts.maxRetry; retryCnt++ {\n\t\t\t\tif ctx.Err() != nil {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\t// prevent infinite blocking\n\t\t\t\tidChan := make(chan clientv3.LeaseID, 1)\n\t\t\t\terrChan := make(chan error, 1)\n\t\t\t\tcancelCtx, cancel := context.WithCancel(ctx)\n\t\t\t\tgo func() {\n\t\t\t\t\tdefer cancel()\n\t\t\t\t\tid, registerErr := r.registerWithKV(cancelCtx, key, value)\n\t\t\t\t\tif registerErr != nil {\n\t\t\t\t\t\terrChan <- registerErr\n\t\t\t\t\t} else {\n\t\t\t\t\t\tidChan <- id\n\t\t\t\t\t}\n\t\t\t\t}()\n\n\t\t\t\tselect {\n\t\t\t\tcase <-time.After(3 * time.Second):\n\t\t\t\t\tcancel()\n\t\t\t\t\tcontinue\n\t\t\t\tcase <-errChan:\n\t\t\t\t\tcontinue\n\t\t\t\tcase curLeaseID = <-idChan:\n\t\t\t\t}\n\n\t\t\t\tkac, err = r.client.KeepAlive(ctx, curLeaseID)\n\t\t\t\tif err == nil {\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t\tretreat = append(retreat, 1<<retryCnt)\n\t\t\t\ttime.Sleep(time.Duration(retreat[randSource.IntN(len(retreat))]) * time.Second)\n\t\t\t}\n\t\t\tif _, ok := <-kac; !ok {\n\t\t\t\t// retry failed\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\n\t\tselect {\n\t\tcase _, ok := <-kac:\n\t\t\tif !ok {\n\t\t\t\tif ctx.Err() != nil {\n\t\t\t\t\t// channel closed due to context cancel\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\t// need to retry registration\n\t\t\t\tcurLeaseID = 0\n\t\t\t\tcontinue\n\t\t\t}\n\t\tcase <-r.opts.ctx.Done():\n\t\t\treturn\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "contrib/registry/etcd/registry_test.go",
    "content": "package etcd\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"testing\"\n\t\"time\"\n\n\t\"google.golang.org/grpc\"\n\n\tclientv3 \"go.etcd.io/etcd/client/v3\"\n\n\t\"github.com/go-kratos/kratos/v2/registry\"\n)\n\nfunc TestRegistry(t *testing.T) {\n\tclient, err := clientv3.New(clientv3.Config{\n\t\tEndpoints:   []string{\"127.0.0.1:2379\"},\n\t\tDialTimeout: time.Second, DialOptions: []grpc.DialOption{grpc.WithBlock()},\n\t})\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tdefer client.Close()\n\n\tctx := context.Background()\n\ts := &registry.ServiceInstance{\n\t\tID:   \"0\",\n\t\tName: \"helloworld\",\n\t}\n\n\tr := New(client)\n\tw, err := r.Watch(ctx, s.Name)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tdefer func() {\n\t\t_ = w.Stop()\n\t}()\n\tgo func() {\n\t\tfor {\n\t\t\tres, err1 := w.Next()\n\t\t\tif err1 != nil {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tt.Logf(\"watch: %d\", len(res))\n\t\t\tfor _, r := range res {\n\t\t\t\tt.Logf(\"next: %+v\", r)\n\t\t\t}\n\t\t}\n\t}()\n\ttime.Sleep(time.Second)\n\n\tif err1 := r.Register(ctx, s); err1 != nil {\n\t\tt.Fatal(err1)\n\t}\n\ttime.Sleep(time.Second)\n\n\tres, err := r.GetService(ctx, s.Name)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif len(res) != 1 && res[0].Name != s.Name {\n\t\tt.Errorf(\"not expected: %+v\", res)\n\t}\n\n\tif err1 := r.Deregister(ctx, s); err1 != nil {\n\t\tt.Fatal(err1)\n\t}\n\ttime.Sleep(time.Second)\n\n\tres, err = r.GetService(ctx, s.Name)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif len(res) != 0 {\n\t\tt.Errorf(\"not expected empty\")\n\t}\n}\n\nfunc TestHeartBeat(t *testing.T) {\n\tclient, err := clientv3.New(clientv3.Config{\n\t\tEndpoints:   []string{\"127.0.0.1:2379\"},\n\t\tDialTimeout: time.Second, DialOptions: []grpc.DialOption{grpc.WithBlock()},\n\t})\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tdefer client.Close()\n\n\tctx := context.Background()\n\ts := &registry.ServiceInstance{\n\t\tID:   \"0\",\n\t\tName: \"helloworld\",\n\t}\n\n\tgo func() {\n\t\tr := New(client)\n\t\tw, err1 := r.Watch(ctx, s.Name)\n\t\tif err1 != nil {\n\t\t\treturn\n\t\t}\n\t\tdefer func() {\n\t\t\t_ = w.Stop()\n\t\t}()\n\t\tfor {\n\t\t\tres, err2 := w.Next()\n\t\t\tif err2 != nil {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tt.Logf(\"watch: %d\", len(res))\n\t\t\tfor _, r := range res {\n\t\t\t\tt.Logf(\"next: %+v\", r)\n\t\t\t}\n\t\t}\n\t}()\n\ttime.Sleep(time.Second)\n\n\t// new a server\n\tr := New(client,\n\t\tRegisterTTL(2*time.Second),\n\t\tMaxRetry(5),\n\t)\n\n\tkey := fmt.Sprintf(\"%s/%s/%s\", r.opts.namespace, s.Name, s.ID)\n\tvalue, _ := marshal(s)\n\tr.lease = clientv3.NewLease(r.client)\n\tleaseID, err := r.registerWithKV(ctx, key, value)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\t// wait for lease expired\n\ttime.Sleep(3 * time.Second)\n\n\tres, err := r.GetService(ctx, s.Name)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif len(res) != 0 {\n\t\tt.Errorf(\"not expected empty\")\n\t}\n\n\tgo r.heartBeat(ctx, leaseID, key, value)\n\n\ttime.Sleep(time.Second)\n\tres, err = r.GetService(ctx, s.Name)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif len(res) == 0 {\n\t\tt.Errorf(\"reconnect failed\")\n\t}\n}\n"
  },
  {
    "path": "contrib/registry/etcd/service.go",
    "content": "package etcd\n\nimport (\n\t\"encoding/json\"\n\n\t\"github.com/go-kratos/kratos/v2/registry\"\n)\n\nfunc marshal(si *registry.ServiceInstance) (string, error) {\n\tdata, err := json.Marshal(si)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\treturn string(data), nil\n}\n\nfunc unmarshal(data []byte) (si *registry.ServiceInstance, err error) {\n\terr = json.Unmarshal(data, &si)\n\treturn\n}\n"
  },
  {
    "path": "contrib/registry/etcd/watcher.go",
    "content": "package etcd\n\nimport (\n\t\"context\"\n\t\"time\"\n\n\tclientv3 \"go.etcd.io/etcd/client/v3\"\n\n\t\"github.com/go-kratos/kratos/v2/registry\"\n)\n\nvar _ registry.Watcher = (*watcher)(nil)\n\ntype watcher struct {\n\tkey         string\n\tctx         context.Context\n\tcancel      context.CancelFunc\n\tclient      *clientv3.Client\n\twatchChan   clientv3.WatchChan\n\twatcher     clientv3.Watcher\n\tkv          clientv3.KV\n\tfirst       bool\n\tserviceName string\n}\n\nfunc newWatcher(ctx context.Context, key, name string, client *clientv3.Client) (*watcher, error) {\n\tw := &watcher{\n\t\tkey:         key,\n\t\tclient:      client,\n\t\twatcher:     clientv3.NewWatcher(client),\n\t\tkv:          clientv3.NewKV(client),\n\t\tfirst:       true,\n\t\tserviceName: name,\n\t}\n\tw.ctx, w.cancel = context.WithCancel(ctx)\n\tw.watchChan = w.watcher.Watch(w.ctx, key, clientv3.WithPrefix(), clientv3.WithRev(0), clientv3.WithKeysOnly())\n\terr := w.watcher.RequestProgress(w.ctx)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn w, nil\n}\n\nfunc (w *watcher) Next() ([]*registry.ServiceInstance, error) {\n\tif w.first {\n\t\titem, err := w.getInstance()\n\t\tw.first = false\n\t\treturn item, err\n\t}\n\n\tselect {\n\tcase <-w.ctx.Done():\n\t\treturn nil, w.ctx.Err()\n\tcase watchResp, ok := <-w.watchChan:\n\t\tif !ok || watchResp.Err() != nil {\n\t\t\ttime.Sleep(time.Second)\n\t\t\terr := w.reWatch()\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t}\n\t\treturn w.getInstance()\n\t}\n}\n\nfunc (w *watcher) Stop() error {\n\tw.cancel()\n\treturn w.watcher.Close()\n}\n\nfunc (w *watcher) getInstance() ([]*registry.ServiceInstance, error) {\n\tresp, err := w.kv.Get(w.ctx, w.key, clientv3.WithPrefix())\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\titems := make([]*registry.ServiceInstance, 0, len(resp.Kvs))\n\tfor _, kv := range resp.Kvs {\n\t\tsi, err := unmarshal(kv.Value)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tif si.Name != w.serviceName {\n\t\t\tcontinue\n\t\t}\n\t\titems = append(items, si)\n\t}\n\treturn items, nil\n}\n\nfunc (w *watcher) reWatch() error {\n\tw.watcher.Close()\n\tw.watcher = clientv3.NewWatcher(w.client)\n\tw.watchChan = w.watcher.Watch(w.ctx, w.key, clientv3.WithPrefix(), clientv3.WithRev(0), clientv3.WithKeysOnly())\n\treturn w.watcher.RequestProgress(w.ctx)\n}\n"
  },
  {
    "path": "contrib/registry/eureka/client.go",
    "content": "package eureka\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"math/rand/v2\"\n\t\"net\"\n\t\"net/http\"\n\t\"strings\"\n\t\"sync\"\n\t\"time\"\n)\n\nconst (\n\tstatusUp           = \"UP\"\n\tstatusDown         = \"DOWN\"\n\tstatusOutOfService = \"OUT_OF_SERVICE\"\n\theartbeatRetry     = 3\n\theartbeatTime      = 10 * time.Second\n\thttpTimeout        = 3 * time.Second\n\trefreshTime        = 30 * time.Second\n)\n\ntype Endpoint struct {\n\tInstanceID     string\n\tIP             string\n\tAppID          string\n\tPort           int\n\tSecurePort     int\n\tHomePageURL    string\n\tStatusPageURL  string\n\tHealthCheckURL string\n\tMetaData       map[string]string\n}\n\n// ApplicationsRootResponse for /eureka/apps\ntype ApplicationsRootResponse struct {\n\tApplicationsResponse `json:\"applications\"`\n}\n\ntype ApplicationsResponse struct {\n\tVersion      string        `json:\"versions__delta\"`\n\tAppsHashcode string        `json:\"apps__hashcode\"`\n\tApplications []Application `json:\"application\"`\n}\n\ntype Application struct {\n\tName     string     `json:\"name\"`\n\tInstance []Instance `json:\"instance\"`\n}\n\ntype RequestInstance struct {\n\tInstance Instance `json:\"instance\"`\n}\n\ntype Instance struct {\n\tInstanceID     string            `json:\"instanceId\"`\n\tHostName       string            `json:\"hostName\"`\n\tPort           Port              `json:\"port\"`\n\tApp            string            `json:\"app\"`\n\tIPAddr         string            `json:\"ipAddr\"`\n\tVipAddress     string            `json:\"vipAddress\"`\n\tStatus         string            `json:\"status\"`\n\tSecurePort     Port              `json:\"securePort\"`\n\tHomePageURL    string            `json:\"homePageUrl\"`\n\tStatusPageURL  string            `json:\"statusPageUrl\"`\n\tHealthCheckURL string            `json:\"healthCheckUrl\"`\n\tDataCenterInfo DataCenterInfo    `json:\"dataCenterInfo\"`\n\tMetadata       map[string]string `json:\"metadata\"`\n}\n\ntype Port struct {\n\tPort    int    `json:\"$\"`\n\tEnabled string `json:\"@enabled\"`\n}\n\ntype DataCenterInfo struct {\n\tName  string `json:\"name\"`\n\tClass string `json:\"@class\"`\n}\n\nvar _ APIInterface = (*Client)(nil)\n\ntype APIInterface interface {\n\tRegister(ctx context.Context, ep Endpoint) error\n\tDeregister(ctx context.Context, appID, instanceID string) error\n\tHeartbeat(ep Endpoint)\n\tFetchApps(ctx context.Context) []Application\n\tFetchAllUpInstances(ctx context.Context) []Instance\n\tFetchAppInstances(ctx context.Context, appID string) (m Application, err error)\n\tFetchAppUpInstances(ctx context.Context, appID string) []Instance\n\tFetchAppInstance(ctx context.Context, appID string, instanceID string) (m Instance, err error)\n\tFetchInstance(ctx context.Context, instanceID string) (m Instance, err error)\n\tOut(ctx context.Context, appID, instanceID string) error\n\tDown(ctx context.Context, appID, instanceID string) error\n}\n\ntype ClientOption func(e *Client)\n\nfunc WithMaxRetry(maxRetry int) ClientOption {\n\treturn func(e *Client) { e.maxRetry = maxRetry }\n}\n\nfunc WithHeartbeatInterval(interval time.Duration) ClientOption {\n\treturn func(e *Client) {\n\t\te.heartbeatInterval = interval\n\t}\n}\n\nfunc WithClientContext(ctx context.Context) ClientOption {\n\treturn func(e *Client) { e.ctx = ctx }\n}\n\nfunc WithNamespace(path string) ClientOption {\n\treturn func(e *Client) { e.eurekaPath = path }\n}\n\ntype Client struct {\n\tctx               context.Context\n\turls              []string\n\teurekaPath        string\n\tmaxRetry          int\n\theartbeatInterval time.Duration\n\tclient            *http.Client\n\tkeepalive         map[string]chan struct{}\n\tlock              sync.Mutex\n}\n\nvar clientTransport = &http.Transport{\n\tDialContext: (&net.Dialer{\n\t\tTimeout:   5 * time.Second,\n\t\tKeepAlive: 30 * time.Second,\n\t}).DialContext,\n\tIdleConnTimeout:       30 * time.Second,\n\tMaxIdleConns:          100,\n\tMaxIdleConnsPerHost:   10,\n\tTLSHandshakeTimeout:   5 * time.Second,\n\tResponseHeaderTimeout: 10 * time.Second,\n}\n\nfunc NewClient(urls []string, opts ...ClientOption) *Client {\n\te := &Client{\n\t\tctx:               context.Background(),\n\t\turls:              urls,\n\t\teurekaPath:        \"eureka/v2\",\n\t\tmaxRetry:          len(urls),\n\t\theartbeatInterval: heartbeatTime,\n\t\tclient:            &http.Client{Transport: clientTransport, Timeout: httpTimeout},\n\t\tkeepalive:         make(map[string]chan struct{}),\n\t}\n\n\tfor _, o := range opts {\n\t\to(e)\n\t}\n\n\treturn e\n}\n\nfunc (e *Client) FetchApps(ctx context.Context) []Application {\n\tvar m ApplicationsRootResponse\n\tif err := e.do(ctx, http.MethodGet, []string{\"apps\"}, nil, &m); err != nil {\n\t\treturn nil\n\t}\n\n\treturn m.Applications\n}\n\nfunc (e *Client) FetchAppInstances(ctx context.Context, appID string) (m Application, err error) {\n\terr = e.do(ctx, http.MethodGet, []string{\"apps\", appID}, nil, &m)\n\treturn\n}\n\nfunc (e *Client) FetchAppUpInstances(ctx context.Context, appID string) []Instance {\n\tapp, err := e.FetchAppInstances(ctx, appID)\n\tif err != nil {\n\t\treturn nil\n\t}\n\treturn e.filterUp(app)\n}\n\nfunc (e *Client) FetchAppInstance(ctx context.Context, appID string, instanceID string) (m Instance, err error) {\n\terr = e.do(ctx, http.MethodGet, []string{\"apps\", appID, instanceID}, nil, &m)\n\treturn\n}\n\nfunc (e *Client) FetchInstance(ctx context.Context, instanceID string) (m Instance, err error) {\n\terr = e.do(ctx, http.MethodGet, []string{\"instances\", instanceID}, nil, &m)\n\treturn\n}\n\nfunc (e *Client) Out(ctx context.Context, appID, instanceID string) error {\n\treturn e.do(ctx, http.MethodPut, []string{\"apps\", appID, instanceID, fmt.Sprintf(\"status?value=%s\", statusOutOfService)}, nil, nil)\n}\n\nfunc (e *Client) Down(ctx context.Context, appID, instanceID string) error {\n\treturn e.do(ctx, http.MethodPut, []string{\"apps\", appID, instanceID, fmt.Sprintf(\"status?value=%s\", statusDown)}, nil, nil)\n}\n\nfunc (e *Client) FetchAllUpInstances(ctx context.Context) []Instance {\n\treturn e.filterUp(e.FetchApps(ctx)...)\n}\n\nfunc (e *Client) Register(ctx context.Context, ep Endpoint) error {\n\treturn e.registerEndpoint(ctx, ep)\n}\n\nfunc (e *Client) Deregister(ctx context.Context, appID, instanceID string) error {\n\tif err := e.do(ctx, http.MethodDelete, []string{\"apps\", appID, instanceID}, nil, nil); err != nil {\n\t\treturn err\n\t}\n\tgo e.cancelHeartbeat(appID)\n\treturn nil\n}\n\nfunc (e *Client) registerEndpoint(ctx context.Context, ep Endpoint) error {\n\tinstance := RequestInstance{\n\t\tInstance: Instance{\n\t\t\tInstanceID: ep.InstanceID,\n\t\t\tHostName:   ep.AppID,\n\t\t\tPort: Port{\n\t\t\t\tPort:    ep.Port,\n\t\t\t\tEnabled: \"true\",\n\t\t\t},\n\t\t\tApp:        ep.AppID,\n\t\t\tIPAddr:     ep.IP,\n\t\t\tVipAddress: ep.AppID,\n\t\t\tStatus:     statusUp,\n\t\t\tSecurePort: Port{\n\t\t\t\tPort:    ep.SecurePort,\n\t\t\t\tEnabled: \"false\",\n\t\t\t},\n\t\t\tHomePageURL:    ep.HomePageURL,\n\t\t\tStatusPageURL:  ep.StatusPageURL,\n\t\t\tHealthCheckURL: ep.HealthCheckURL,\n\t\t\tDataCenterInfo: DataCenterInfo{\n\t\t\t\tName:  \"MyOwn\",\n\t\t\t\tClass: \"com.netflix.appinfo.InstanceInfo$DefaultDataCenterInfo\",\n\t\t\t},\n\t\t\tMetadata: ep.MetaData,\n\t\t},\n\t}\n\n\tbody, err := json.Marshal(instance)\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn e.do(ctx, http.MethodPost, []string{\"apps\", ep.AppID}, bytes.NewReader(body), nil)\n}\n\nfunc (e *Client) Heartbeat(ep Endpoint) {\n\te.lock.Lock()\n\te.keepalive[ep.AppID] = make(chan struct{})\n\te.lock.Unlock()\n\n\tticker := time.NewTicker(e.heartbeatInterval)\n\tdefer ticker.Stop()\n\tretryCount := 0\n\tfor {\n\t\tselect {\n\t\tcase <-e.ctx.Done():\n\t\t\treturn\n\t\tcase <-e.keepalive[ep.AppID]:\n\t\t\treturn\n\t\tcase <-ticker.C:\n\t\t\tif err := e.do(e.ctx, http.MethodPut, []string{\"apps\", ep.AppID, ep.InstanceID}, nil, nil); err != nil {\n\t\t\t\tif retryCount++; retryCount > heartbeatRetry {\n\t\t\t\t\t_ = e.registerEndpoint(e.ctx, ep)\n\t\t\t\t\tretryCount = 0\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc (e *Client) cancelHeartbeat(appID string) {\n\te.lock.Lock()\n\tdefer e.lock.Unlock()\n\tif ch, ok := e.keepalive[appID]; ok {\n\t\tch <- struct{}{}\n\t}\n}\n\nfunc (e *Client) filterUp(apps ...Application) (res []Instance) {\n\tfor _, app := range apps {\n\t\tfor _, ins := range app.Instance {\n\t\t\tif ins.Status == statusUp {\n\t\t\t\tres = append(res, ins)\n\t\t\t}\n\t\t}\n\t}\n\n\treturn\n}\n\nfunc (e *Client) pickServer(currentTimes int) string {\n\treturn e.urls[currentTimes%e.maxRetry]\n}\n\nfunc (e *Client) shuffle() {\n\trand.New(rand.NewPCG(uint64(time.Now().UnixNano()), 0)).\n\t\tShuffle(len(e.urls), func(i, j int) {\n\t\t\te.urls[i], e.urls[j] = e.urls[j], e.urls[i]\n\t\t})\n}\n\nfunc (e *Client) buildAPI(currentTimes int, params ...string) string {\n\tif currentTimes == 0 {\n\t\te.shuffle()\n\t}\n\tserver := e.pickServer(currentTimes)\n\tparams = append([]string{server, e.eurekaPath}, params...)\n\treturn strings.Join(params, \"/\")\n}\n\nfunc (e *Client) request(ctx context.Context, method string, params []string, input io.Reader, output any, i int) (bool, error) {\n\trequest, err := http.NewRequestWithContext(ctx, method, e.buildAPI(i, params...), input)\n\tif err != nil {\n\t\treturn false, err\n\t}\n\trequest.Header.Add(\"User-Agent\", \"go-eureka-client\")\n\trequest.Header.Add(\"Accept\", \"application/json;charset=UTF-8\")\n\trequest.Header.Add(\"Content-Type\", \"application/json;charset=UTF-8\")\n\tresp, err := e.client.Do(request)\n\tif err != nil {\n\t\treturn true, err\n\t}\n\tdefer func() {\n\t\t_, _ = io.Copy(io.Discard, resp.Body)\n\t\t_ = resp.Body.Close()\n\t}()\n\n\tif output != nil && resp.StatusCode/100 == 2 {\n\t\tdata, err := io.ReadAll(resp.Body)\n\t\tif err != nil {\n\t\t\treturn false, err\n\t\t}\n\t\terr = json.Unmarshal(data, output)\n\t\tif err != nil {\n\t\t\treturn false, err\n\t\t}\n\t}\n\n\tif resp.StatusCode >= http.StatusBadRequest {\n\t\treturn false, fmt.Errorf(\"response Error %d\", resp.StatusCode)\n\t}\n\n\treturn false, nil\n}\n\nfunc (e *Client) do(ctx context.Context, method string, params []string, input io.Reader, output any) error {\n\tfor i := 0; i < e.maxRetry; i++ {\n\t\tretry, err := e.request(ctx, method, params, input, output, i)\n\t\tif retry {\n\t\t\tcontinue\n\t\t}\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\treturn nil\n\t}\n\treturn fmt.Errorf(\"retry after %d times\", e.maxRetry)\n}\n"
  },
  {
    "path": "contrib/registry/eureka/eureka.go",
    "content": "package eureka\n\nimport (\n\t\"context\"\n\t\"strings\"\n\t\"sync\"\n\t\"time\"\n)\n\ntype subscriber struct {\n\tappID    string\n\tcallBack func()\n}\n\ntype API struct {\n\tcli             *Client\n\tallInstances    map[string][]Instance\n\tsubscribers     map[string]*subscriber\n\trefreshInterval time.Duration\n\tlock            sync.Mutex\n}\n\nfunc NewAPI(ctx context.Context, client *Client, refreshInterval time.Duration) *API {\n\te := &API{\n\t\tcli:             client,\n\t\tallInstances:    make(map[string][]Instance),\n\t\tsubscribers:     make(map[string]*subscriber),\n\t\trefreshInterval: refreshInterval,\n\t}\n\n\t// it is required to broadcast for the first time\n\tgo e.broadcast()\n\n\tgo e.refresh(ctx)\n\n\treturn e\n}\n\nfunc (e *API) refresh(ctx context.Context) {\n\tticker := time.NewTicker(e.refreshInterval)\n\tdefer ticker.Stop()\n\tfor {\n\t\tselect {\n\t\tcase <-ctx.Done():\n\t\t\treturn\n\t\tcase <-ticker.C:\n\t\t\te.broadcast()\n\t\t}\n\t}\n}\n\nfunc (e *API) broadcast() {\n\tinstances := e.cacheAllInstances()\n\tif instances == nil {\n\t\treturn\n\t}\n\n\tfor _, subscriber := range e.subscribers {\n\t\tgo subscriber.callBack()\n\t}\n\te.lock.Lock()\n\te.allInstances = instances\n\te.lock.Unlock()\n}\n\nfunc (e *API) cacheAllInstances() map[string][]Instance {\n\titems := make(map[string][]Instance)\n\tinstances := e.cli.FetchAllUpInstances(context.Background())\n\tfor _, instance := range instances {\n\t\titems[e.ToAppID(instance.App)] = append(items[instance.App], instance)\n\t}\n\n\treturn items\n}\n\nfunc (e *API) Register(ctx context.Context, serviceName string, endpoints ...Endpoint) error {\n\tappID := e.ToAppID(serviceName)\n\tupInstances := make(map[string]struct{})\n\n\tfor _, ins := range e.GetService(ctx, appID) {\n\t\tupInstances[ins.InstanceID] = struct{}{}\n\t}\n\n\tfor _, ep := range endpoints {\n\t\tif _, ok := upInstances[ep.InstanceID]; !ok {\n\t\t\tif err := e.cli.Register(ctx, ep); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tgo e.cli.Heartbeat(ep)\n\t\t}\n\t}\n\n\treturn nil\n}\n\n// Deregister ctx is the same as register ctx\nfunc (e *API) Deregister(ctx context.Context, endpoints []Endpoint) error {\n\tfor _, ep := range endpoints {\n\t\tif err := e.cli.Deregister(ctx, ep.AppID, ep.InstanceID); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\treturn nil\n}\n\nfunc (e *API) Subscribe(serverName string, fn func()) error {\n\te.lock.Lock()\n\tappID := e.ToAppID(serverName)\n\te.subscribers[appID] = &subscriber{\n\t\tappID:    appID,\n\t\tcallBack: fn,\n\t}\n\te.lock.Unlock()\n\tgo e.broadcast()\n\treturn nil\n}\n\nfunc (e *API) GetService(ctx context.Context, serverName string) []Instance {\n\tappID := e.ToAppID(serverName)\n\tif ins, ok := e.allInstances[appID]; ok {\n\t\treturn ins\n\t}\n\n\t// if not in allInstances of API, you can try to obtain it separately again\n\treturn e.cli.FetchAppUpInstances(ctx, appID)\n}\n\nfunc (e *API) Unsubscribe(serverName string) {\n\te.lock.Lock()\n\tdelete(e.subscribers, e.ToAppID(serverName))\n\te.lock.Unlock()\n}\n\nfunc (e *API) ToAppID(serverName string) string {\n\treturn strings.ToUpper(serverName)\n}\n"
  },
  {
    "path": "contrib/registry/eureka/go.mod",
    "content": "module github.com/go-kratos/kratos/contrib/registry/eureka/v2\n\ngo 1.22\n\nrequire github.com/go-kratos/kratos/v2 v2.9.2\n\nreplace github.com/go-kratos/kratos/v2 => ../../../\n"
  },
  {
    "path": "contrib/registry/eureka/go.sum",
    "content": ""
  },
  {
    "path": "contrib/registry/eureka/register.go",
    "content": "package eureka\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/go-kratos/kratos/v2/registry\"\n)\n\nvar (\n\t_ registry.Registrar = (*Registry)(nil)\n\t_ registry.Discovery = (*Registry)(nil)\n)\n\ntype Option func(o *Registry)\n\n// WithContext with registry context.\nfunc WithContext(ctx context.Context) Option {\n\treturn func(o *Registry) { o.ctx = ctx }\n}\n\nfunc WithHeartbeat(interval time.Duration) Option {\n\treturn func(o *Registry) { o.heartbeatInterval = interval }\n}\n\nfunc WithRefresh(interval time.Duration) Option {\n\treturn func(o *Registry) { o.refreshInterval = interval }\n}\n\nfunc WithEurekaPath(path string) Option {\n\treturn func(o *Registry) { o.eurekaPath = path }\n}\n\ntype Registry struct {\n\tctx               context.Context\n\tapi               *API\n\theartbeatInterval time.Duration\n\trefreshInterval   time.Duration\n\teurekaPath        string\n}\n\nfunc New(eurekaUrls []string, opts ...Option) (*Registry, error) {\n\tr := &Registry{\n\t\tctx:               context.Background(),\n\t\theartbeatInterval: heartbeatTime,\n\t\trefreshInterval:   refreshTime,\n\t\teurekaPath:        \"eureka/v2\",\n\t}\n\n\tfor _, o := range opts {\n\t\to(r)\n\t}\n\n\tclient := NewClient(eurekaUrls,\n\t\tWithHeartbeatInterval(r.heartbeatInterval),\n\t\tWithClientContext(r.ctx),\n\t\tWithNamespace(r.eurekaPath),\n\t)\n\tr.api = NewAPI(r.ctx, client, r.refreshInterval)\n\treturn r, nil\n}\n\n// Register registers the service instance.\n// The context here is exclusive to each registry instance.\nfunc (r *Registry) Register(ctx context.Context, service *registry.ServiceInstance) error {\n\treturn r.api.Register(ctx, service.Name, r.Endpoints(service)...)\n}\n\n// Deregister deregisters the service instance from Eureka.\nfunc (r *Registry) Deregister(ctx context.Context, service *registry.ServiceInstance) error {\n\treturn r.api.Deregister(ctx, r.Endpoints(service))\n}\n\n// GetService gets services from Eureka.\nfunc (r *Registry) GetService(ctx context.Context, serviceName string) ([]*registry.ServiceInstance, error) {\n\tinstances := r.api.GetService(ctx, serviceName)\n\titems := make([]*registry.ServiceInstance, 0, len(instances))\n\tfor _, instance := range instances {\n\t\titems = append(items, &registry.ServiceInstance{\n\t\t\tID:        instance.Metadata[\"ID\"],\n\t\t\tName:      instance.Metadata[\"Name\"],\n\t\t\tVersion:   instance.Metadata[\"Version\"],\n\t\t\tEndpoints: []string{instance.Metadata[\"Endpoints\"]},\n\t\t\tMetadata:  instance.Metadata,\n\t\t})\n\t}\n\n\treturn items, nil\n}\n\n// Watch creates a watcher for the service. It uses an independent context.\nfunc (r *Registry) Watch(ctx context.Context, serviceName string) (registry.Watcher, error) {\n\treturn newWatch(ctx, r.api, serviceName)\n}\n\nfunc (r *Registry) Endpoints(service *registry.ServiceInstance) []Endpoint {\n\tres := make([]Endpoint, 0, len(service.Endpoints))\n\tfor _, ep := range service.Endpoints {\n\t\tstart := strings.Index(ep, \"//\")\n\t\tend := strings.LastIndex(ep, \":\")\n\t\tappID := strings.ToUpper(service.Name)\n\t\tip := ep[start+2 : end]\n\t\tsport := ep[end+1:]\n\t\tport, _ := strconv.Atoi(sport)\n\t\tsecurePort := 443\n\t\thomePageURL := fmt.Sprintf(\"%s/\", ep)\n\t\tstatusPageURL := fmt.Sprintf(\"%s/info\", ep)\n\t\thealthCheckURL := fmt.Sprintf(\"%s/health\", ep)\n\t\tinstanceID := strings.Join([]string{ip, appID, sport}, \":\")\n\t\tmetadata := make(map[string]string)\n\t\tif len(service.Metadata) > 0 {\n\t\t\tmetadata = service.Metadata\n\t\t}\n\t\tif s, ok := service.Metadata[\"securePort\"]; ok {\n\t\t\tsecurePort, _ = strconv.Atoi(s)\n\t\t}\n\t\tif s, ok := service.Metadata[\"homePageURL\"]; ok {\n\t\t\thomePageURL = s\n\t\t}\n\t\tif s, ok := service.Metadata[\"statusPageURL\"]; ok {\n\t\t\tstatusPageURL = s\n\t\t}\n\t\tif s, ok := service.Metadata[\"healthCheckURL\"]; ok {\n\t\t\thealthCheckURL = s\n\t\t}\n\t\tmetadata[\"ID\"] = service.ID\n\t\tmetadata[\"Name\"] = service.Name\n\t\tmetadata[\"Version\"] = service.Version\n\t\tmetadata[\"Endpoints\"] = ep\n\t\tmetadata[\"agent\"] = \"go-eureka-client\"\n\t\tres = append(res, Endpoint{\n\t\t\tAppID:          appID,\n\t\t\tIP:             ip,\n\t\t\tPort:           port,\n\t\t\tSecurePort:     securePort,\n\t\t\tHomePageURL:    homePageURL,\n\t\t\tStatusPageURL:  statusPageURL,\n\t\t\tHealthCheckURL: healthCheckURL,\n\t\t\tInstanceID:     instanceID,\n\t\t\tMetaData:       metadata,\n\t\t})\n\t}\n\n\treturn res\n}\n"
  },
  {
    "path": "contrib/registry/eureka/register_test.go",
    "content": "package eureka\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"log\"\n\t\"sync\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/go-kratos/kratos/v2/registry\"\n)\n\nfunc TestRegistry(_ *testing.T) {\n\tctx := context.Background()\n\tctx, cancel := context.WithCancel(ctx)\n\ts1 := &registry.ServiceInstance{\n\t\tID:        \"0\",\n\t\tName:      \"helloworld\",\n\t\tEndpoints: []string{\"http://127.0.0.1:1111\"},\n\t}\n\ts2 := &registry.ServiceInstance{\n\t\tID:        \"0\",\n\t\tName:      \"helloworld2\",\n\t\tEndpoints: []string{\"http://127.0.0.1:222\"},\n\t}\n\n\tr, _ := New([]string{\"https://127.0.0.1:18761\"}, WithContext(ctx), WithHeartbeat(time.Second), WithRefresh(time.Second), WithEurekaPath(\"eureka\"))\n\n\tgo do(r, s1)\n\tgo do(r, s2)\n\n\ttime.Sleep(time.Second * 20)\n\tcancel()\n\ttime.Sleep(time.Second * 1)\n}\n\nfunc do(r *Registry, s *registry.ServiceInstance) {\n\tw, err := r.Watch(context.Background(), s.Name)\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\tdefer func() {\n\t\t_ = w.Stop()\n\t}()\n\tgo func() {\n\t\tfor {\n\t\t\tres, nextErr := w.Next()\n\t\t\tif nextErr != nil {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tlog.Printf(\"watch: %d\", len(res))\n\t\t\tfor _, r := range res {\n\t\t\t\tlog.Printf(\"next: %+v\", r)\n\t\t\t}\n\t\t}\n\t}()\n\n\tctx, cancel := context.WithCancel(context.Background())\n\tif err = r.Register(ctx, s); err != nil {\n\t\tlog.Fatal(err)\n\t}\n\n\ttime.Sleep(time.Second * 10)\n\tres, err := r.GetService(ctx, s.Name)\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\tfor i, re := range res {\n\t\tlog.Printf(\"first %d re:%v\\n\", i, re)\n\t}\n\n\tif len(res) != 1 && res[0].Name != s.Name {\n\t\tlog.Fatalf(\"not expected: %+v\", res)\n\t}\n\n\tif err = r.Deregister(ctx, s); err != nil {\n\t\tlog.Fatal(err)\n\t}\n\tcancel()\n\ttime.Sleep(time.Second * 10)\n\n\tres, err = r.GetService(ctx, s.Name)\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\tfor i, re := range res {\n\t\tlog.Printf(\"second %d re:%v\\n\", i, re)\n\t}\n\tif len(res) != 0 {\n\t\tlog.Fatalf(\"not expected empty\")\n\t}\n}\n\nfunc TestLock(_ *testing.T) {\n\ttype me struct {\n\t\tlock sync.Mutex\n\t}\n\n\ta := &me{}\n\tgo func() {\n\t\tdefer a.lock.Unlock()\n\t\ta.lock.Lock()\n\t\tfmt.Println(\"This is fmt first.\")\n\t\ttime.Sleep(time.Second * 5)\n\t}()\n\tgo func() {\n\t\tdefer a.lock.Unlock()\n\t\ta.lock.Lock()\n\t\tfmt.Println(\"This is fmt second.\")\n\t\ttime.Sleep(time.Second * 5)\n\t}()\n\ttime.Sleep(time.Second * 10)\n}\n"
  },
  {
    "path": "contrib/registry/eureka/watcher.go",
    "content": "package eureka\n\nimport (\n\t\"context\"\n\n\t\"github.com/go-kratos/kratos/v2/registry\"\n)\n\nvar _ registry.Watcher = (*watcher)(nil)\n\ntype watcher struct {\n\tctx        context.Context\n\tcancel     context.CancelFunc\n\tcli        *API\n\twatchChan  chan struct{}\n\tserverName string\n}\n\nfunc newWatch(ctx context.Context, cli *API, serverName string) (*watcher, error) {\n\tw := &watcher{\n\t\tctx:        ctx,\n\t\tcli:        cli,\n\t\tserverName: serverName,\n\t\twatchChan:  make(chan struct{}, 1),\n\t}\n\tw.ctx, w.cancel = context.WithCancel(ctx)\n\te := w.cli.Subscribe(\n\t\tserverName,\n\t\tfunc() {\n\t\t\tw.watchChan <- struct{}{}\n\t\t},\n\t)\n\treturn w, e\n}\n\nfunc (w *watcher) Next() (services []*registry.ServiceInstance, err error) {\n\tselect {\n\tcase <-w.ctx.Done():\n\t\treturn nil, w.ctx.Err()\n\tcase <-w.watchChan:\n\t\tinstances := w.cli.GetService(w.ctx, w.serverName)\n\t\tservices = make([]*registry.ServiceInstance, 0, len(instances))\n\t\tfor _, instance := range instances {\n\t\t\tservices = append(services, &registry.ServiceInstance{\n\t\t\t\tID:        instance.Metadata[\"ID\"],\n\t\t\t\tName:      instance.Metadata[\"Name\"],\n\t\t\t\tVersion:   instance.Metadata[\"Version\"],\n\t\t\t\tEndpoints: []string{instance.Metadata[\"Endpoints\"]},\n\t\t\t\tMetadata:  instance.Metadata,\n\t\t\t})\n\t\t}\n\t\treturn\n\t}\n}\n\nfunc (w *watcher) Stop() error {\n\tw.cancel()\n\tw.cli.Unsubscribe(w.serverName)\n\treturn nil\n}\n"
  },
  {
    "path": "contrib/registry/kubernetes/go.mod",
    "content": "module github.com/go-kratos/kratos/contrib/registry/kubernetes/v2\n\ngo 1.22\n\nrequire (\n\tgithub.com/go-kratos/kratos/v2 v2.9.2\n\tgithub.com/json-iterator/go v1.1.12\n\tk8s.io/api v0.24.3\n\tk8s.io/apimachinery v0.24.3\n\tk8s.io/client-go v0.24.3\n)\n\nrequire (\n\tgithub.com/PuerkitoBio/purell v1.1.1 // indirect\n\tgithub.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect\n\tgithub.com/davecgh/go-spew v1.1.1 // indirect\n\tgithub.com/emicklei/go-restful v2.16.0+incompatible // indirect\n\tgithub.com/go-logr/logr v1.4.1 // indirect\n\tgithub.com/go-openapi/jsonpointer v0.19.5 // indirect\n\tgithub.com/go-openapi/jsonreference v0.19.5 // indirect\n\tgithub.com/go-openapi/swag v0.19.14 // indirect\n\tgithub.com/gogo/protobuf v1.3.2 // indirect\n\tgithub.com/golang/protobuf v1.5.4 // indirect\n\tgithub.com/google/gnostic v0.5.7-v3refs // indirect\n\tgithub.com/google/go-cmp v0.5.5 // indirect\n\tgithub.com/google/gofuzz v1.1.0 // indirect\n\tgithub.com/imdario/mergo v0.3.5 // indirect\n\tgithub.com/josharian/intern v1.0.0 // indirect\n\tgithub.com/mailru/easyjson v0.7.6 // 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/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect\n\tgithub.com/spf13/pflag v1.0.5 // indirect\n\tgolang.org/x/net v0.33.0 // indirect\n\tgolang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 // indirect\n\tgolang.org/x/sys v0.28.0 // indirect\n\tgolang.org/x/term v0.27.0 // indirect\n\tgolang.org/x/text v0.21.0 // indirect\n\tgolang.org/x/time v0.0.0-20220210224613-90d013bbcef8 // indirect\n\tgoogle.golang.org/appengine v1.6.7 // indirect\n\tgoogle.golang.org/protobuf v1.33.0 // indirect\n\tgopkg.in/inf.v0 v0.9.1 // indirect\n\tgopkg.in/yaml.v2 v2.4.0 // indirect\n\tgopkg.in/yaml.v3 v3.0.1 // indirect\n\tk8s.io/klog/v2 v2.60.1 // indirect\n\tk8s.io/kube-openapi v0.0.0-20220328201542-3ee0da9b0b42 // indirect\n\tk8s.io/utils v0.0.0-20220210201930-3a6ce19ff2f9 // indirect\n\tsigs.k8s.io/json v0.0.0-20211208200746-9f7c6b3444d2 // indirect\n\tsigs.k8s.io/structured-merge-diff/v4 v4.2.1 // indirect\n\tsigs.k8s.io/yaml v1.2.0 // indirect\n)\n\nreplace github.com/go-kratos/kratos/v2 => ../../../\n"
  },
  {
    "path": "contrib/registry/kubernetes/go.sum",
    "content": "cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=\ncloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=\ncloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=\ncloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=\ncloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=\ncloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=\ncloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=\ncloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To=\ncloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4=\ncloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M=\ncloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc=\ncloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk=\ncloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs=\ncloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc=\ncloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY=\ncloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI=\ncloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk=\ncloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg=\ncloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8=\ncloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0=\ncloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=\ncloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=\ncloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=\ncloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=\ncloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=\ncloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=\ncloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=\ncloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=\ncloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=\ncloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=\ncloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=\ncloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU=\ncloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=\ncloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=\ncloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=\ncloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=\ncloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=\ndmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=\ngithub.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=\ngithub.com/Azure/go-autorest/autorest v0.11.18/go.mod h1:dSiJPy22c3u0OtOKDNttNgqpNFY/GeWa7GH/Pz56QRA=\ngithub.com/Azure/go-autorest/autorest/adal v0.9.13/go.mod h1:W/MM4U6nLxnIskrw4UwWzlHfGjwUS50aOsc/I3yuU8M=\ngithub.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74=\ngithub.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k=\ngithub.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8=\ngithub.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU=\ngithub.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=\ngithub.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=\ngithub.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ=\ngithub.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI=\ngithub.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=\ngithub.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M=\ngithub.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=\ngithub.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=\ngithub.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=\ngithub.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=\ngithub.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=\ngithub.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=\ngithub.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=\ngithub.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=\ngithub.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=\ngithub.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=\ngithub.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=\ngithub.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=\ngithub.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.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/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE=\ngithub.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc=\ngithub.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=\ngithub.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=\ngithub.com/emicklei/go-restful v2.16.0+incompatible h1:rgqiKNjTnFQA6kkhFe16D8epTksy9HQ1MyrbDXSdYhM=\ngithub.com/emicklei/go-restful v2.16.0+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=\ngithub.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=\ngithub.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=\ngithub.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=\ngithub.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po=\ngithub.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=\ngithub.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=\ngithub.com/evanphx/json-patch v4.12.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=\ngithub.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k=\ngithub.com/form3tech-oss/jwt-go v3.2.3+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k=\ngithub.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=\ngithub.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=\ngithub.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=\ngithub.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=\ngithub.com/getkin/kin-openapi v0.76.0/go.mod h1:660oXbgy5JFMKreazJaQTw7o+X00qeSyhcnluiMv+Xg=\ngithub.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=\ngithub.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=\ngithub.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=\ngithub.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=\ngithub.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas=\ngithub.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU=\ngithub.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=\ngithub.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ=\ngithub.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=\ngithub.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=\ngithub.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY=\ngithub.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=\ngithub.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8=\ngithub.com/go-openapi/jsonreference v0.19.5 h1:1WJP/wi4OjB4iV8KVbH73rQaoialJrqv8gitZLxGLtM=\ngithub.com/go-openapi/jsonreference v0.19.5/go.mod h1:RdybgQwPxbL4UEjuAruzK1x3nE69AqPYEJeo/TWfEeg=\ngithub.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=\ngithub.com/go-openapi/swag v0.19.14 h1:gm3vOOXfiuw5i9p5N9xJvfjvuofpyvLA9Wr6QfK5Fng=\ngithub.com/go-openapi/swag v0.19.14/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ=\ngithub.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=\ngithub.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=\ngithub.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=\ngithub.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=\ngithub.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=\ngithub.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=\ngithub.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=\ngithub.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=\ngithub.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=\ngithub.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=\ngithub.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=\ngithub.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=\ngithub.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=\ngithub.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=\ngithub.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8=\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.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=\ngithub.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=\ngithub.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=\ngithub.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=\ngithub.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=\ngithub.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=\ngithub.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=\ngithub.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=\ngithub.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=\ngithub.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=\ngithub.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=\ngithub.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=\ngithub.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM=\ngithub.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=\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.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=\ngithub.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA=\ngithub.com/google/gnostic v0.5.7-v3refs h1:FhTMOKj2VhjpouxvWJAV1TL304uMlb9zcDqkl6cEI54=\ngithub.com/google/gnostic v0.5.7-v3refs/go.mod h1:73MKFl6jIHelAJNaBGFzt3SPtZULs9dYrGFt8OiIsHQ=\ngithub.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=\ngithub.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=\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.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=\ngithub.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=\ngithub.com/google/gofuzz v1.1.0 h1:Hsa8mG0dQ46ij8Sl2AYJDUv1oA9/d6Vk+3LG99Oe02g=\ngithub.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=\ngithub.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=\ngithub.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=\ngithub.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=\ngithub.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=\ngithub.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=\ngithub.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=\ngithub.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=\ngithub.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=\ngithub.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=\ngithub.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=\ngithub.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=\ngithub.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=\ngithub.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=\ngithub.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=\ngithub.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=\ngithub.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=\ngithub.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4=\ngithub.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=\ngithub.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=\ngithub.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=\ngithub.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=\ngithub.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=\ngithub.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=\ngithub.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=\ngithub.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=\ngithub.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=\ngithub.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=\ngithub.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=\ngithub.com/imdario/mergo v0.3.5 h1:JboBksRwiiAJWvIYJVo46AfV+IAIKZpfrSzVKj42R4Q=\ngithub.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=\ngithub.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=\ngithub.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=\ngithub.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=\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/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=\ngithub.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=\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/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=\ngithub.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=\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/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=\ngithub.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=\ngithub.com/mailru/easyjson v0.7.6 h1:8yTIVnZgCoiM1TgqoeTl+LfU5Jg6/xL3QhGQnimLYnA=\ngithub.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=\ngithub.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=\ngithub.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c=\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 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/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=\ngithub.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=\ngithub.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=\ngithub.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=\ngithub.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=\ngithub.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78=\ngithub.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=\ngithub.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=\ngithub.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=\ngithub.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=\ngithub.com/onsi/ginkgo v1.14.0 h1:2mOpI4JVVPBN+WQRa0WKH2eXR+Ey+uK4n7Zj0aYpIQA=\ngithub.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=\ngithub.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=\ngithub.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=\ngithub.com/onsi/gomega v1.10.1 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE=\ngithub.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=\ngithub.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=\ngithub.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=\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/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=\ngithub.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=\ngithub.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=\ngithub.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=\ngithub.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=\ngithub.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=\ngithub.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=\ngithub.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8=\ngithub.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\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.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=\ngithub.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngithub.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=\ngithub.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngithub.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=\ngithub.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=\ngithub.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=\ngithub.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=\ngithub.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=\ngo.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=\ngo.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=\ngo.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=\ngo.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=\ngo.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=\ngo.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=\ngo.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=\ngolang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=\ngolang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\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.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=\ngolang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=\ngolang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=\ngolang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=\ngolang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=\ngolang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=\ngolang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=\ngolang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=\ngolang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=\ngolang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=\ngolang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=\ngolang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=\ngolang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=\ngolang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=\ngolang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=\ngolang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=\ngolang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=\ngolang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=\ngolang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=\ngolang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=\ngolang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=\ngolang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=\ngolang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=\ngolang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=\ngolang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=\ngolang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=\ngolang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=\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/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=\ngolang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=\ngolang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=\ngolang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=\ngolang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20200222125558-5a598a2470a0/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-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=\ngolang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=\ngolang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=\ngolang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=\ngolang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=\ngolang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=\ngolang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=\ngolang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=\ngolang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=\ngolang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=\ngolang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=\ngolang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=\ngolang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=\ngolang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=\ngolang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=\ngolang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc=\ngolang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=\ngolang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=\ngolang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=\ngolang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I=\ngolang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=\ngolang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=\ngolang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=\ngolang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=\ngolang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=\ngolang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=\ngolang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=\ngolang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=\ngolang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=\ngolang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=\ngolang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=\ngolang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=\ngolang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 h1:RerP+noqYHUQ8CMRcPlC2nvTa4dcBIjegkuWdcUDuqg=\ngolang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=\ngolang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\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-20190227155943-e225da77a7e6/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-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/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-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/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-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/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-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=\ngolang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=\ngolang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=\ngolang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=\ngolang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q=\ngolang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM=\ngolang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/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.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\ngolang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\ngolang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\ngolang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=\ngolang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=\ngolang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=\ngolang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=\ngolang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=\ngolang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=\ngolang.org/x/time v0.0.0-20220210224613-90d013bbcef8 h1:vVKdlvoWBphwdxWKrFZEuM0kGgGLxUOYcY4U/2Vjg44=\ngolang.org/x/time v0.0.0-20220210224613-90d013bbcef8/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=\ngolang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=\ngolang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=\ngolang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=\ngolang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=\ngolang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=\ngolang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=\ngolang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=\ngolang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=\ngolang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=\ngolang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=\ngolang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/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-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=\ngolang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=\ngolang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=\ngolang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=\ngolang.org/x/tools v0.0.0-20200505023115-26f46d2f7ef8/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=\ngolang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=\ngolang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=\ngolang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=\ngolang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=\ngolang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=\ngolang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=\ngolang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=\ngolang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE=\ngolang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=\ngolang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=\ngolang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=\ngolang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=\ngolang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=\ngolang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=\ngolang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=\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 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=\ngolang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngoogle.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=\ngoogle.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=\ngoogle.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=\ngoogle.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=\ngoogle.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=\ngoogle.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=\ngoogle.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=\ngoogle.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=\ngoogle.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=\ngoogle.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=\ngoogle.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=\ngoogle.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=\ngoogle.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=\ngoogle.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=\ngoogle.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM=\ngoogle.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc=\ngoogle.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg=\ngoogle.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE=\ngoogle.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8=\ngoogle.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU=\ngoogle.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94=\ngoogle.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=\ngoogle.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=\ngoogle.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=\ngoogle.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=\ngoogle.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=\ngoogle.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=\ngoogle.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=\ngoogle.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=\ngoogle.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=\ngoogle.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=\ngoogle.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=\ngoogle.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=\ngoogle.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=\ngoogle.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=\ngoogle.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=\ngoogle.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=\ngoogle.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=\ngoogle.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=\ngoogle.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=\ngoogle.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=\ngoogle.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=\ngoogle.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=\ngoogle.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA=\ngoogle.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U=\ngoogle.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=\ngoogle.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=\ngoogle.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=\ngoogle.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=\ngoogle.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=\ngoogle.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=\ngoogle.golang.org/genproto v0.0.0-20201019141844-1ed22bb0c154/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=\ngoogle.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=\ngoogle.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=\ngoogle.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=\ngoogle.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=\ngoogle.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=\ngoogle.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=\ngoogle.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=\ngoogle.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=\ngoogle.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A=\ngoogle.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=\ngoogle.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=\ngoogle.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=\ngoogle.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=\ngoogle.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=\ngoogle.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=\ngoogle.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=\ngoogle.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=\ngoogle.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60=\ngoogle.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=\ngoogle.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=\ngoogle.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=\ngoogle.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=\ngoogle.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=\ngoogle.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8=\ngoogle.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=\ngoogle.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=\ngoogle.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=\ngoogle.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=\ngoogle.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=\ngoogle.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=\ngoogle.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=\ngoogle.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=\ngoogle.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=\ngoogle.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=\ngoogle.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=\ngoogle.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=\ngoogle.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=\ngoogle.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=\ngoogle.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=\ngoogle.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=\ngoogle.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=\ngoogle.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=\ngopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/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-20200227125254-8fa46927fb4f/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/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=\ngopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=\ngopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=\ngopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=\ngopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=\ngopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=\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.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=\ngopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=\ngopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\ngopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\ngopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\ngopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=\ngopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\nhonnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\nhonnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\nhonnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\nhonnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\nhonnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=\nhonnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=\nhonnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=\nk8s.io/api v0.24.3 h1:tt55QEmKd6L2k5DP6G/ZzdMQKvG5ro4H4teClqm0sTY=\nk8s.io/api v0.24.3/go.mod h1:elGR/XSZrS7z7cSZPzVWaycpJuGIw57j9b95/1PdJNI=\nk8s.io/apimachinery v0.24.3 h1:hrFiNSA2cBZqllakVYyH/VyEh4B581bQRmqATJSeQTg=\nk8s.io/apimachinery v0.24.3/go.mod h1:82Bi4sCzVBdpYjyI4jY6aHX+YCUchUIrZrXKedjd2UM=\nk8s.io/client-go v0.24.3 h1:Nl1840+6p4JqkFWEW2LnMKU667BUxw03REfLAVhuKQY=\nk8s.io/client-go v0.24.3/go.mod h1:AAovolf5Z9bY1wIg2FZ8LPQlEdKHjLI7ZD4rw920BJw=\nk8s.io/gengo v0.0.0-20210813121822-485abfe95c7c/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E=\nk8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE=\nk8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y=\nk8s.io/klog/v2 v2.60.1 h1:VW25q3bZx9uE3vvdL6M8ezOX79vA2Aq1nEWLqNQclHc=\nk8s.io/klog/v2 v2.60.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0=\nk8s.io/kube-openapi v0.0.0-20220328201542-3ee0da9b0b42 h1:Gii5eqf+GmIEwGNKQYQClCayuJCe2/4fZUvF7VG99sU=\nk8s.io/kube-openapi v0.0.0-20220328201542-3ee0da9b0b42/go.mod h1:Z/45zLw8lUo4wdiUkI+v/ImEGAvu3WatcZl3lPMR4Rk=\nk8s.io/utils v0.0.0-20210802155522-efc7438f0176/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=\nk8s.io/utils v0.0.0-20220210201930-3a6ce19ff2f9 h1:HNSDgDCrr/6Ly3WEGKZftiE7IY19Vz2GdbOCyI4qqhc=\nk8s.io/utils v0.0.0-20220210201930-3a6ce19ff2f9/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=\nrsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=\nrsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=\nrsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=\nsigs.k8s.io/json v0.0.0-20211208200746-9f7c6b3444d2 h1:kDi4JBNAsJWfz1aEXhO8Jg87JJaPNLh5tIzYHgStQ9Y=\nsigs.k8s.io/json v0.0.0-20211208200746-9f7c6b3444d2/go.mod h1:B+TnT182UBxE84DiCz4CVE26eOSDAeYCpfDnC2kdKMY=\nsigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw=\nsigs.k8s.io/structured-merge-diff/v4 v4.2.1 h1:bKCqE9GvQ5tiVHn5rfn1r+yao3aLQEaLzkkmAkf+A6Y=\nsigs.k8s.io/structured-merge-diff/v4 v4.2.1/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4=\nsigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q=\nsigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=\n"
  },
  {
    "path": "contrib/registry/kubernetes/registry.go",
    "content": "// Package kuberegistry registry simply implements the Kubernetes-based Registry\npackage kuberegistry\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"net\"\n\t\"net/url\"\n\t\"os\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n\n\tjsoniter \"github.com/json-iterator/go\"\n\tcorev1 \"k8s.io/api/core/v1\"\n\tmetav1 \"k8s.io/apimachinery/pkg/apis/meta/v1\"\n\t\"k8s.io/apimachinery/pkg/labels\"\n\t\"k8s.io/apimachinery/pkg/types\"\n\t\"k8s.io/client-go/informers\"\n\t\"k8s.io/client-go/kubernetes\"\n\tlisterv1 \"k8s.io/client-go/listers/core/v1\"\n\t\"k8s.io/client-go/tools/cache\"\n\n\t\"github.com/go-kratos/kratos/v2/registry\"\n)\n\n// Defines the key name of specific fields\n// Kratos needs to cooperate with the following fields to run properly on Kubernetes:\n// kratos-service-id: define the ID of the service\n// kratos-service-app: define the name of the service\n// kratos-service-version: define the version of the service\n// kratos-service-metadata: define the metadata of the service\n// kratos-service-protocols: define the protocols of the service\n//\n// Example Deployment:\n/*\napiVersion: apps/v1\nkind: Deployment\nmetadata:\nname: nginx\nlabels:\n  app: nginx\nspec:\nreplicas: 5\nselector:\n  matchLabels:\n    app: nginx\ntemplate:\n  metadata:\n    labels:\n      app: nginx\n      kratos-service-id: \"56991810-c77f-4a95-8190-393efa9c1a61\"\n      kratos-service-app: \"nginx\"\n      kratos-service-version: \"v3.5.0\"\n    annotations:\n      kratos-service-protocols: |\n        {\"80\": \"http\"}\n      kratos-service-metadata: |\n        {\"region\": \"sh\", \"zone\": \"sh001\", \"cluster\": \"pd\"}\n  spec:\n    containers:\n      - name: nginx\n        image: nginx:1.7.9\n        ports:\n          - containerPort: 80\n*/\nconst (\n\t// LabelsKeyServiceID is used to define the ID of the service\n\tLabelsKeyServiceID = \"kratos-service-id\"\n\t// LabelsKeyServiceName is used to define the name of the service\n\tLabelsKeyServiceName = \"kratos-service-app\"\n\t// LabelsKeyServiceVersion is used to define the version of the service\n\tLabelsKeyServiceVersion = \"kratos-service-version\"\n\t// AnnotationsKeyMetadata is used to define the metadata of the service\n\tAnnotationsKeyMetadata = \"kratos-service-metadata\"\n\t// AnnotationsKeyProtocolMap is used to define the protocols of the service\n\t// Through the value of this field, Kratos can obtain the application layer protocol corresponding to the port\n\t// Example value: {\"80\": \"http\", \"8081\": \"grpc\"}\n\tAnnotationsKeyProtocolMap = \"kratos-service-protocols\"\n)\n\n// The Registry simply implements service discovery based on Kubernetes\n// It has not been verified in the production environment and is currently for reference only\ntype Registry struct {\n\tclientSet       *kubernetes.Clientset\n\tinformerFactory informers.SharedInformerFactory\n\tpodInformer     cache.SharedIndexInformer\n\tpodLister       listerv1.PodLister\n\n\tstopCh chan struct{}\n}\n\n// NewRegistry is used to initialize the Registry\nfunc NewRegistry(clientSet *kubernetes.Clientset, namespace string) *Registry {\n\tif namespace == \"\" {\n\t\tnamespace = metav1.NamespaceAll\n\t}\n\tinformerFactory := informers.NewSharedInformerFactoryWithOptions(clientSet, time.Minute*10, informers.WithNamespace(namespace))\n\tpodInformer := informerFactory.Core().V1().Pods().Informer()\n\tpodLister := informerFactory.Core().V1().Pods().Lister()\n\treturn &Registry{\n\t\tclientSet:       clientSet,\n\t\tinformerFactory: informerFactory,\n\t\tpodInformer:     podInformer,\n\t\tpodLister:       podLister,\n\t\tstopCh:          make(chan struct{}),\n\t}\n}\n\n// Register is used to register services\n// Note that on Kubernetes, it can only be used to update the id/name/version/metadata/protocols of the current service,\n// but it cannot be used to update node.\nfunc (s *Registry) Register(ctx context.Context, service *registry.ServiceInstance) error {\n\t// GetMetadata\n\tmetadataVal, err := marshal(service.Metadata)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t// Generate ProtocolMap\n\tprotocolMap, err := getProtocolMapByEndpoints(service.Endpoints)\n\tif err != nil {\n\t\treturn err\n\t}\n\tprotocolMapVal, err := marshal(protocolMap)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tpatchBytes, err := jsoniter.Marshal(map[string]any{\n\t\t\"metadata\": metav1.ObjectMeta{\n\t\t\tLabels: map[string]string{\n\t\t\t\tLabelsKeyServiceID:      service.ID,\n\t\t\t\tLabelsKeyServiceName:    service.Name,\n\t\t\t\tLabelsKeyServiceVersion: service.Version,\n\t\t\t},\n\t\t\tAnnotations: map[string]string{\n\t\t\t\tAnnotationsKeyMetadata:    metadataVal,\n\t\t\t\tAnnotationsKeyProtocolMap: protocolMapVal,\n\t\t\t},\n\t\t},\n\t})\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif _, err = s.clientSet.\n\t\tCoreV1().\n\t\tPods(GetNamespace()).\n\t\tPatch(ctx, GetPodName(), types.StrategicMergePatchType, patchBytes, metav1.PatchOptions{}); err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\n// Deregister the registration.\nfunc (s *Registry) Deregister(ctx context.Context, _ *registry.ServiceInstance) error {\n\treturn s.Register(ctx, &registry.ServiceInstance{\n\t\tMetadata: map[string]string{},\n\t})\n}\n\n// GetService return the service instances in memory according to the service name.\nfunc (s *Registry) GetService(_ context.Context, name string) ([]*registry.ServiceInstance, error) {\n\tpods, err := s.podLister.List(labels.SelectorFromSet(map[string]string{\n\t\tLabelsKeyServiceName: name,\n\t}))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tret := make([]*registry.ServiceInstance, 0, len(pods))\n\tfor _, pod := range pods {\n\t\tif pod.Status.Phase != corev1.PodRunning {\n\t\t\tcontinue\n\t\t}\n\t\tinstance, err := getServiceInstanceFromPod(pod)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tret = append(ret, instance)\n\t}\n\treturn ret, nil\n}\n\nfunc (s *Registry) sendLatestInstances(ctx context.Context, name string, announcement chan []*registry.ServiceInstance) {\n\tinstances, err := s.GetService(ctx, name)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tannouncement <- instances\n}\n\n// Watch creates a watcher according to the service name.\nfunc (s *Registry) Watch(ctx context.Context, name string) (registry.Watcher, error) {\n\tstopCh := make(chan struct{}, 1)\n\tannouncement := make(chan []*registry.ServiceInstance, 1)\n\ts.podInformer.AddEventHandler(cache.FilteringResourceEventHandler{\n\t\tFilterFunc: func(obj any) bool {\n\t\t\tselect {\n\t\t\tcase <-stopCh:\n\t\t\t\treturn false\n\t\t\tcase <-s.stopCh:\n\t\t\t\treturn false\n\t\t\tdefault:\n\t\t\t\tpod := obj.(*corev1.Pod)\n\t\t\t\tval := pod.GetLabels()[LabelsKeyServiceName]\n\t\t\t\treturn val == name\n\t\t\t}\n\t\t},\n\t\tHandler: cache.ResourceEventHandlerFuncs{\n\t\t\tAddFunc: func(any) {\n\t\t\t\ts.sendLatestInstances(ctx, name, announcement)\n\t\t\t},\n\t\t\tUpdateFunc: func(any, any) {\n\t\t\t\ts.sendLatestInstances(ctx, name, announcement)\n\t\t\t},\n\t\t\tDeleteFunc: func(any) {\n\t\t\t\ts.sendLatestInstances(ctx, name, announcement)\n\t\t\t},\n\t\t},\n\t})\n\treturn NewIterator(announcement, stopCh), nil\n}\n\n// Start is used to start the Registry\n// It is non-blocking\nfunc (s *Registry) Start() {\n\ts.informerFactory.Start(s.stopCh)\n\tif !cache.WaitForCacheSync(s.stopCh, s.podInformer.HasSynced) {\n\t\treturn\n\t}\n}\n\n// Close is used to close the Registry\n// After closing, any callbacks generated by Watch will not be executed\nfunc (s *Registry) Close() {\n\tselect {\n\tcase <-s.stopCh:\n\tdefault:\n\t\tclose(s.stopCh)\n\t}\n}\n\n// //////////// K8S Runtime ////////////\n\n// ServiceAccountNamespacePath defines the location of the namespace file\nconst ServiceAccountNamespacePath = \"/var/run/secrets/kubernetes.io/serviceaccount/namespace\"\n\nvar currentNamespace = LoadNamespace()\n\n// LoadNamespace is used to get the current namespace from the file\nfunc LoadNamespace() string {\n\tdata, err := os.ReadFile(ServiceAccountNamespacePath)\n\tif err != nil {\n\t\treturn \"\"\n\t}\n\treturn string(data)\n}\n\n// GetNamespace is used to get the namespace of the Pod where the current container is located\nfunc GetNamespace() string {\n\treturn currentNamespace\n}\n\n// GetPodName is used to get the name of the Pod where the current container is located\nfunc GetPodName() string {\n\treturn os.Getenv(\"HOSTNAME\")\n}\n\n// //////////// ProtocolMap ////////////\n\ntype protocolMap map[string]string\n\nfunc (m protocolMap) GetProtocol(port int32) string {\n\treturn m[strconv.Itoa(int(port))]\n}\n\n// //////////// Iterator ////////////\n\n// Iterator performs the conversion from channel to iterator\n// It reads the latest changes from the `chan []*registry.ServiceInstance`\n// And the outside can sense the closure of Iterator through stopCh\ntype Iterator struct {\n\tch     chan []*registry.ServiceInstance\n\tstopCh chan struct{}\n}\n\n// NewIterator is used to initialize Iterator\nfunc NewIterator(channel chan []*registry.ServiceInstance, stopCh chan struct{}) *Iterator {\n\treturn &Iterator{\n\t\tch:     channel,\n\t\tstopCh: stopCh,\n\t}\n}\n\n// Next will block until ServiceInstance changes\nfunc (iter *Iterator) Next() ([]*registry.ServiceInstance, error) {\n\tselect {\n\tcase instances := <-iter.ch:\n\t\treturn instances, nil\n\tcase <-iter.stopCh:\n\t\treturn nil, ErrIteratorClosed\n\t}\n}\n\n// Stop is used to close the iterator\nfunc (iter *Iterator) Stop() error {\n\tselect {\n\tcase <-iter.stopCh:\n\tdefault:\n\t\tclose(iter.stopCh)\n\t}\n\treturn nil\n}\n\n// //////////// Helper Func ////////////\n\nfunc marshal(in any) (string, error) {\n\treturn jsoniter.MarshalToString(in)\n}\n\nfunc unmarshal(data string, in any) error {\n\treturn jsoniter.UnmarshalFromString(data, in)\n}\n\nfunc isEmptyObjectString(s string) bool {\n\tswitch s {\n\tcase \"\", \"{}\", \"null\", \"nil\", \"[]\":\n\t\treturn true\n\t}\n\treturn false\n}\n\nfunc getProtocolMapByEndpoints(endpoints []string) (protocolMap, error) {\n\tret := protocolMap{}\n\tfor _, endpoint := range endpoints {\n\t\tu, err := url.Parse(endpoint)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tret[u.Port()] = u.Scheme\n\t}\n\treturn ret, nil\n}\n\nfunc getProtocolMapFromPod(pod *corev1.Pod) (protocolMap, error) {\n\tprotoMap := protocolMap{}\n\tif s := pod.Annotations[AnnotationsKeyProtocolMap]; !isEmptyObjectString(s) {\n\t\terr := unmarshal(s, &protoMap)\n\t\tif err != nil {\n\t\t\treturn nil, &ErrorHandleResource{Namespace: pod.Namespace, Name: pod.Name, Reason: err}\n\t\t}\n\t}\n\treturn protoMap, nil\n}\n\nfunc getMetadataFromPod(pod *corev1.Pod) (map[string]string, error) {\n\tmetadata := map[string]string{}\n\tif s := pod.Annotations[AnnotationsKeyMetadata]; !isEmptyObjectString(s) {\n\t\terr := unmarshal(s, &metadata)\n\t\tif err != nil {\n\t\t\treturn nil, &ErrorHandleResource{Namespace: pod.Namespace, Name: pod.Name, Reason: err}\n\t\t}\n\t}\n\treturn metadata, nil\n}\n\nfunc getServiceInstanceFromPod(pod *corev1.Pod) (*registry.ServiceInstance, error) {\n\tpodIP := pod.Status.PodIP\n\tpodLabels := pod.GetLabels()\n\t// Get Metadata\n\tmetadata, err := getMetadataFromPod(pod)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\t// Get Protocols Definition\n\tprotocolMap, err := getProtocolMapFromPod(pod)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\t// Get Endpoints\n\tvar endpoints []string\n\tfor _, container := range pod.Spec.Containers {\n\t\tfor _, cp := range container.Ports {\n\t\t\tport := cp.ContainerPort\n\t\t\tprotocol := protocolMap.GetProtocol(port)\n\t\t\tif protocol == \"\" {\n\t\t\t\tif cp.Name != \"\" {\n\t\t\t\t\tprotocol = strings.Split(cp.Name, \"-\")[0]\n\t\t\t\t} else {\n\t\t\t\t\tprotocol = string(cp.Protocol)\n\t\t\t\t}\n\t\t\t}\n\t\t\taddr := protocol + \"://\" + net.JoinHostPort(podIP, strconv.Itoa(int(port)))\n\t\t\tendpoints = append(endpoints, addr)\n\t\t}\n\t}\n\treturn &registry.ServiceInstance{\n\t\tID:        podLabels[LabelsKeyServiceID],\n\t\tName:      podLabels[LabelsKeyServiceName],\n\t\tVersion:   podLabels[LabelsKeyServiceVersion],\n\t\tMetadata:  metadata,\n\t\tEndpoints: endpoints,\n\t}, nil\n}\n\n// //////////// Error Definition ////////////\n\n// ErrIteratorClosed defines the error that the iterator is closed\nvar ErrIteratorClosed = errors.New(\"iterator closed\")\n\n// ErrorHandleResource defines the error that cannot handle K8S resources normally\ntype ErrorHandleResource struct {\n\tNamespace string\n\tName      string\n\tReason    error\n}\n\n// Error implements the error interface\nfunc (err *ErrorHandleResource) Error() string {\n\treturn fmt.Sprintf(\"failed to handle resource(namespace=%s, name=%s): %s\",\n\t\terr.Namespace, err.Name, err.Reason)\n}\n"
  },
  {
    "path": "contrib/registry/kubernetes/registry_test.go",
    "content": "package kuberegistry\n\nimport (\n\t\"context\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/go-kratos/kratos/v2/registry\"\n\n\tappsv1 \"k8s.io/api/apps/v1\"\n\tapiv1 \"k8s.io/api/core/v1\"\n\tmetav1 \"k8s.io/apimachinery/pkg/apis/meta/v1\"\n\t\"k8s.io/client-go/kubernetes\"\n\t\"k8s.io/client-go/rest\"\n\t\"k8s.io/client-go/tools/clientcmd\"\n\t\"k8s.io/client-go/util/homedir\"\n)\n\nconst (\n\tnamespace  = \"default\"\n\tdeployName = \"hello-deployment\"\n\tpodName    = \"hello\"\n)\n\nvar deployment = appsv1.Deployment{\n\tObjectMeta: metav1.ObjectMeta{\n\t\tName: deployName,\n\t},\n\tSpec: appsv1.DeploymentSpec{\n\t\tReplicas: int32Ptr(1),\n\t\tSelector: &metav1.LabelSelector{\n\t\t\tMatchLabels: map[string]string{\n\t\t\t\t\"app\": podName,\n\t\t\t},\n\t\t},\n\t\tTemplate: apiv1.PodTemplateSpec{\n\t\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\t\tLabels: map[string]string{\n\t\t\t\t\t\"app\": podName,\n\t\t\t\t},\n\t\t\t},\n\t\t\tSpec: apiv1.PodSpec{\n\t\t\t\tContainers: []apiv1.Container{\n\t\t\t\t\t{\n\t\t\t\t\t\tName:  \"nginx\",\n\t\t\t\t\t\tImage: \"nginx:alpine\",\n\t\t\t\t\t\tPorts: []apiv1.ContainerPort{\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tName:          \"http\",\n\t\t\t\t\t\t\t\tProtocol:      apiv1.ProtocolTCP,\n\t\t\t\t\t\t\t\tContainerPort: 80,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t\tCommand: []string{\n\t\t\t\t\t\t\t\"nginx\",\n\t\t\t\t\t\t\t\"-g\",\n\t\t\t\t\t\t\t\"daemon off;\",\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t},\n}\n\nfunc getClientSet() (*kubernetes.Clientset, error) {\n\trestConfig, err := rest.InClusterConfig()\n\thome := homedir.HomeDir()\n\n\tif err != nil {\n\t\tkubeconfig := filepath.Join(home, \".kube\", \"config\")\n\t\trestConfig, err = clientcmd.BuildConfigFromFlags(\"\", kubeconfig)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\tclientSet, err := kubernetes.NewForConfig(restConfig)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn clientSet, nil\n}\n\nfunc int32Ptr(i int32) *int32 { return &i }\n\nfunc TestSetEnv(t *testing.T) {\n\tos.Setenv(\"HOSTNAME\", podName)\n\tif os.Getenv(\"HOSTNAME\") != podName {\n\t\tt.Fatal(\"error\")\n\t}\n}\n\nfunc TestRegistry(t *testing.T) {\n\tcurrentNamespace = \"default\"\n\n\tclientSet, err := getClientSet()\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tr := NewRegistry(clientSet, currentNamespace)\n\tr.Start()\n\n\tsvrHello := &registry.ServiceInstance{\n\t\tID:        \"1\",\n\t\tName:      \"hello\",\n\t\tVersion:   \"v1.0.0\",\n\t\tEndpoints: []string{\"http://127.0.0.1:80\"},\n\t}\n\t_, err = clientSet.AppsV1().Deployments(namespace).Create(context.Background(), &deployment, metav1.CreateOptions{})\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\twatch, err := r.Watch(context.Background(), svrHello.Name)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tdefer func() {\n\t\t_ = watch.Stop()\n\t}()\n\n\tgo func() {\n\t\tfor {\n\t\t\tres, err1 := watch.Next()\n\t\t\tif err1 != nil {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tt.Logf(\"watch: %d\", len(res))\n\t\t\tfor _, r := range res {\n\t\t\t\tt.Logf(\"next: %+v\", r)\n\t\t\t}\n\t\t}\n\t}()\n\ttime.Sleep(time.Second)\n\n\tpod, err := clientSet.CoreV1().Pods(namespace).List(context.Background(), metav1.ListOptions{\n\t\tLabelSelector: \"app=hello\",\n\t})\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tif len(pod.Items) < 1 {\n\t\tt.Fatal(\"fetch resource error\")\n\t}\n\n\tos.Setenv(\"HOSTNAME\", pod.Items[0].Name)\n\n\t// Always remember delete test resource\n\tdefer func() {\n\t\t_ = clientSet.AppsV1().Deployments(namespace).Delete(context.Background(), deployName, metav1.DeleteOptions{})\n\t}()\n\n\tif err = r.Register(context.Background(), svrHello); err != nil {\n\t\tt.Fatal(err)\n\t}\n\ttime.Sleep(time.Second)\n\n\tres, err := r.GetService(context.Background(), svrHello.Name)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tif len(res) != 1 && res[0].Name != svrHello.Name {\n\t\tt.Fatal(err)\n\t}\n\n\tif err1 := r.Deregister(context.Background(), svrHello); err1 != nil {\n\t\tt.Fatal(err1)\n\t}\n\n\ttime.Sleep(time.Second)\n\n\tres, err = r.GetService(context.Background(), svrHello.Name)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif len(res) != 0 {\n\t\tt.Fatal(\"not expected empty\")\n\t}\n}\n"
  },
  {
    "path": "contrib/registry/nacos/README.md",
    "content": "# Nacos Registry\n\n## example\n### server\n```go\npackage main\n\nimport (\n\t\"log\"\n\n\t\"github.com/nacos-group/nacos-sdk-go/clients\"\n\t\"github.com/nacos-group/nacos-sdk-go/common/constant\"\n\t\"github.com/nacos-group/nacos-sdk-go/vo\"\n\n\t\"github.com/go-kratos/kratos/contrib/registry/nacos/v2\"\n\t\"github.com/go-kratos/kratos/v2\"\n)\n\nfunc main() {\n\tsc := []constant.ServerConfig{\n\t\t*constant.NewServerConfig(\"127.0.0.1\", 8848),\n\t}\n\n\tclient, err := clients.NewNamingClient(\n\t\tvo.NacosClientParam{\n\t\t\tServerConfigs: sc,\n\t\t},\n\t)\n\n\tif err != nil {\n\t\tlog.Panic(err)\n\t}\n\n\tr := nacos.New(client)\n\n\t// server\n\tapp := kratos.New(\n\t\tkratos.Name(\"helloworld\"),\n\t\tkratos.Registrar(r),\n\t)\n\tif err := app.Run(); err != nil {\n\t\tlog.Fatal(err)\n\t}\n}\n```\n### client\n```go\npackage main\n\nimport (\n\t\"context\"\n\t\"log\"\n\n\t\"github.com/nacos-group/nacos-sdk-go/clients\"\n\t\"github.com/nacos-group/nacos-sdk-go/common/constant\"\n\t\"github.com/nacos-group/nacos-sdk-go/vo\"\n\n\t\"github.com/go-kratos/kratos/contrib/registry/nacos/v2\"\n\t\"github.com/go-kratos/kratos/v2/transport/grpc\"\n)\n\nfunc main() {\n\tcc := constant.ClientConfig{\n\t\tNamespaceId: \"public\",\n\t\tTimeoutMs:   5000,\n\t}\n\n\tclient, err := clients.NewNamingClient(\n\t\tvo.NacosClientParam{\n\t\t\tClientConfig: &cc,\n\t\t},\n\t)\n\n\tif err != nil {\n\t\tlog.Panic(err)\n\t}\n\n\tr := nacos.New(client)\n\n\t// client\n\tconn, err := grpc.DialInsecure(\n\t\tcontext.Background(),\n\t\tgrpc.WithEndpoint(\"discovery:///helloworld\"),\n\t\tgrpc.WithDiscovery(r),\n\t)\n\tdefer conn.Close()\n}\n```\n"
  },
  {
    "path": "contrib/registry/nacos/go.mod",
    "content": "module github.com/go-kratos/kratos/contrib/registry/nacos/v2\n\ngo 1.22\n\nrequire (\n\tgithub.com/go-kratos/kratos/v2 v2.9.2\n\tgithub.com/nacos-group/nacos-sdk-go v1.0.9\n)\n\nrequire (\n\tgithub.com/aliyun/alibaba-cloud-sdk-go v1.61.18 // indirect\n\tgithub.com/buger/jsonparser v1.1.1 // indirect\n\tgithub.com/go-errors/errors v1.0.1 // indirect\n\tgithub.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af // indirect\n\tgithub.com/json-iterator/go v1.1.6 // indirect\n\tgithub.com/lestrrat/go-file-rotatelogs v0.0.0-20180223000712-d3151e2a480f // indirect\n\tgithub.com/lestrrat/go-strftime v0.0.0-20180220042222-ba3bf9c1d042 // indirect\n\tgithub.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect\n\tgithub.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 // indirect\n\tgithub.com/pkg/errors v0.9.1 // indirect\n\tgithub.com/toolkits/concurrent v0.0.0-20150624120057-a4371d70e3e3 // indirect\n\tgo.uber.org/atomic v1.6.0 // indirect\n\tgo.uber.org/multierr v1.5.0 // indirect\n\tgo.uber.org/zap v1.15.0 // indirect\n\tgopkg.in/ini.v1 v1.42.0 // indirect\n)\n\nreplace github.com/go-kratos/kratos/v2 => ../../../\n\nreplace github.com/buger/jsonparser => github.com/buger/jsonparser v1.1.1\n"
  },
  {
    "path": "contrib/registry/nacos/go.sum",
    "content": "github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=\ngithub.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=\ngithub.com/aliyun/alibaba-cloud-sdk-go v1.61.18 h1:zOVTBdCKFd9JbCKz9/nt+FovbjPFmb7mUnp8nH9fQBA=\ngithub.com/aliyun/alibaba-cloud-sdk-go v1.61.18/go.mod h1:v8ESoHo4SyHmuB4b1tJqDHxfTGEciD+yhvOU/5s1Rfk=\ngithub.com/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMUs=\ngithub.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0=\ngithub.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.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/fastly/go-utils v0.0.0-20180712184237-d95a45783239 h1:Ghm4eQYC0nEPnSJdVkTrXpu9KtoVCSo1hg7mtI7G9KU=\ngithub.com/fastly/go-utils v0.0.0-20180712184237-d95a45783239/go.mod h1:Gdwt2ce0yfBxPvZrHkprdPPTTS3N5rwmLE8T22KBXlw=\ngithub.com/go-errors/errors v1.0.1 h1:LUHzmkK3GUKUrL/1gfBUxAHzcev3apQlezX/+O7ma6w=\ngithub.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=\ngithub.com/goji/httpauth v0.0.0-20160601135302-2da839ab0f4d/go.mod h1:nnjvkQ9ptGaCkuDUx6wNykzzlUixGxvkme+H/lnzb+A=\ngithub.com/golang/mock v1.3.1 h1:qGJ6qTW+x6xX/my+8YUVl4WNpX9B7+/l2tRsHGZ7f2s=\ngithub.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=\ngithub.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=\ngithub.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=\ngithub.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=\ngithub.com/jehiah/go-strftime v0.0.0-20171201141054-1d33003b3869 h1:IPJ3dvxmJ4uczJe5YQdrYB16oTJlGSC/OyZDqUk9xX4=\ngithub.com/jehiah/go-strftime v0.0.0-20171201141054-1d33003b3869/go.mod h1:cJ6Cj7dQo+O6GJNiMx+Pa94qKj+TG8ONdKHgMNIyyag=\ngithub.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af h1:pmfjZENx5imkbgOkpRUYLnmbU7UEFbjtDA2hxJ1ichM=\ngithub.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=\ngithub.com/jonboulle/clockwork v0.1.0 h1:VKV+ZcuP6l3yW9doeqz6ziZGgcynBVQO+obU0+0hcPo=\ngithub.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=\ngithub.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=\ngithub.com/json-iterator/go v1.1.6 h1:MrUvLMLTMxbqFJ9kzlvat/rYZqZnW3u4wkLzWTaFwKs=\ngithub.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=\ngithub.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=\ngithub.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=\ngithub.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=\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 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=\ngithub.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=\ngithub.com/lestrrat/go-envload v0.0.0-20180220120943-6ed08b54a570 h1:0iQektZGS248WXmGIYOwRXSQhD4qn3icjMpuxwO7qlo=\ngithub.com/lestrrat/go-envload v0.0.0-20180220120943-6ed08b54a570/go.mod h1:BLt8L9ld7wVsvEWQbuLrUZnCMnUmLZ+CGDzKtclrTlE=\ngithub.com/lestrrat/go-file-rotatelogs v0.0.0-20180223000712-d3151e2a480f h1:sgUSP4zdTUZYZgAGGtN5Lxk92rK+JUFOwf+FT99EEI4=\ngithub.com/lestrrat/go-file-rotatelogs v0.0.0-20180223000712-d3151e2a480f/go.mod h1:UGmTpUd3rjbtfIpwAPrcfmGf/Z1HS95TATB+m57TPB8=\ngithub.com/lestrrat/go-strftime v0.0.0-20180220042222-ba3bf9c1d042 h1:Bvq8AziQ5jFF4BHGAEDSqwPW1NJS3XshxbRCxtjFAZc=\ngithub.com/lestrrat/go-strftime v0.0.0-20180220042222-ba3bf9c1d042/go.mod h1:TPpsiPUEh0zFL1Snz4crhMlBe60PYxRHr5oFF3rRYg0=\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 h1:Esafd1046DLDQ0W1YjYsBW+p8U2u7vzgW2SQVmlNazg=\ngithub.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=\ngithub.com/nacos-group/nacos-sdk-go v1.0.9 h1:sMvrp6tZj4LdhuHRsS4GCqASB81k3pjmT2ykDQQpwt0=\ngithub.com/nacos-group/nacos-sdk-go v1.0.9/go.mod h1:hlAPn3UdzlxIlSILAyOXKxjFSvDJ9oLzTJ9hLAK1KzA=\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 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=\ngithub.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=\ngithub.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=\ngithub.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=\ngithub.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=\ngithub.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=\ngithub.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=\ngithub.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a h1:pa8hGb/2YqsZKovtsgrwcDH1RZhVbTKCjLp47XpqCDs=\ngithub.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=\ngithub.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\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.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=\ngithub.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=\ngithub.com/tebeka/strftime v0.1.3 h1:5HQXOqWKYRFfNyBMNVc9z5+QzuBtIXy03psIhtdJYto=\ngithub.com/tebeka/strftime v0.1.3/go.mod h1:7wJm3dZlpr4l/oVK0t1HYIc4rMzQ2XJlOMIUJUJH6XQ=\ngithub.com/toolkits/concurrent v0.0.0-20150624120057-a4371d70e3e3 h1:kF/7m/ZU+0D4Jj5eZ41Zm3IH/J8OElK1Qtd7tVKAwLk=\ngithub.com/toolkits/concurrent v0.0.0-20150624120057-a4371d70e3e3/go.mod h1:QDlpd3qS71vYtakd2hmdpqhJ9nwv6mD6A30bQ1BPBFE=\ngo.uber.org/atomic v1.6.0 h1:Ezj3JGmsOnG1MoRWQkPBsKLe9DwWD9QeXzTRzzldNVk=\ngo.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=\ngo.uber.org/multierr v1.5.0 h1:KCa4XfM8CWFCpxXRGok+Q0SS/0XBhMDbHHGABQLvD2A=\ngo.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU=\ngo.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee h1:0mgffUl7nfd+FpvXMVz4IDEaUSmT1ysygQC7qYo7sG4=\ngo.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=\ngo.uber.org/zap v1.15.0 h1:ZZCA22JRF2gQE5FoNmhmrf7jeJJ2uhqDUNRYKm8dvmM=\ngo.uber.org/zap v1.15.0/go.mod h1:Mb2vm2krFEG5DV0W9qcHBYFtp/Wku1cvYaqPsS/WYfc=\ngolang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=\ngolang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/lint v0.0.0-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA6JiaEZDtJb9Ei+1LlBs=\ngolang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=\ngolang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I=\ngolang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=\ngolang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=\ngolang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=\ngolang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=\ngolang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=\ngolang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5 h1:hKsoRgsbwY1NafxrwTs+k64bikrLBkAgPir1TNCj3Zs=\ngolang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/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/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=\ngopkg.in/ini.v1 v1.42.0 h1:7N3gPTt50s8GuLortA00n8AqRTk75qOP98+mTPpgzRk=\ngopkg.in/ini.v1 v1.42.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=\ngopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=\ngopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\nhonnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM=\nhonnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=\n"
  },
  {
    "path": "contrib/registry/nacos/registry.go",
    "content": "package nacos\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"maps\"\n\t\"math\"\n\t\"net\"\n\t\"net/url\"\n\t\"strconv\"\n\n\t\"github.com/nacos-group/nacos-sdk-go/clients/naming_client\"\n\t\"github.com/nacos-group/nacos-sdk-go/common/constant\"\n\t\"github.com/nacos-group/nacos-sdk-go/vo\"\n\n\t\"github.com/go-kratos/kratos/v2/registry\"\n)\n\nvar ErrServiceInstanceNameEmpty = errors.New(\"kratos/nacos: ServiceInstance.Name can not be empty\")\n\nvar (\n\t_ registry.Registrar = (*Registry)(nil)\n\t_ registry.Discovery = (*Registry)(nil)\n)\n\ntype options struct {\n\tprefix  string\n\tweight  float64\n\tcluster string\n\tgroup   string\n\tkind    string\n}\n\n// Option is nacos option.\ntype Option func(o *options)\n\n// WithPrefix with prefix path.\nfunc WithPrefix(prefix string) Option {\n\treturn func(o *options) { o.prefix = prefix }\n}\n\n// WithWeight with weight option.\nfunc WithWeight(weight float64) Option {\n\treturn func(o *options) { o.weight = weight }\n}\n\n// WithCluster with cluster option.\nfunc WithCluster(cluster string) Option {\n\treturn func(o *options) { o.cluster = cluster }\n}\n\n// WithGroup with group option.\nfunc WithGroup(group string) Option {\n\treturn func(o *options) { o.group = group }\n}\n\n// WithDefaultKind with default kind option.\nfunc WithDefaultKind(kind string) Option {\n\treturn func(o *options) { o.kind = kind }\n}\n\n// Registry is nacos registry.\ntype Registry struct {\n\topts options\n\tcli  naming_client.INamingClient\n}\n\n// New new a nacos registry.\nfunc New(cli naming_client.INamingClient, opts ...Option) (r *Registry) {\n\top := options{\n\t\tprefix:  \"/microservices\",\n\t\tcluster: \"DEFAULT\",\n\t\tgroup:   constant.DEFAULT_GROUP,\n\t\tweight:  100,\n\t\tkind:    \"grpc\",\n\t}\n\tfor _, option := range opts {\n\t\toption(&op)\n\t}\n\treturn &Registry{\n\t\topts: op,\n\t\tcli:  cli,\n\t}\n}\n\n// Register the registration.\nfunc (r *Registry) Register(_ context.Context, si *registry.ServiceInstance) error {\n\tif si.Name == \"\" {\n\t\treturn ErrServiceInstanceNameEmpty\n\t}\n\tfor _, endpoint := range si.Endpoints {\n\t\tu, err := url.Parse(endpoint)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\thost, port, err := net.SplitHostPort(u.Host)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tp, err := strconv.Atoi(port)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tweight := r.opts.weight\n\t\tvar rmd map[string]string\n\t\tif si.Metadata == nil {\n\t\t\trmd = map[string]string{\n\t\t\t\t\"kind\":    u.Scheme,\n\t\t\t\t\"version\": si.Version,\n\t\t\t}\n\t\t} else {\n\t\t\trmd = maps.Clone(si.Metadata)\n\t\t\trmd[\"kind\"] = u.Scheme\n\t\t\trmd[\"version\"] = si.Version\n\t\t\tif w, ok := si.Metadata[\"weight\"]; ok {\n\t\t\t\tweight, err = strconv.ParseFloat(w, 64)\n\t\t\t\tif err != nil {\n\t\t\t\t\tweight = r.opts.weight\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t_, e := r.cli.RegisterInstance(vo.RegisterInstanceParam{\n\t\t\tIp:          host,\n\t\t\tPort:        uint64(p),\n\t\t\tServiceName: si.Name + \".\" + u.Scheme,\n\t\t\tWeight:      weight,\n\t\t\tEnable:      true,\n\t\t\tHealthy:     true,\n\t\t\tEphemeral:   true,\n\t\t\tMetadata:    rmd,\n\t\t\tClusterName: r.opts.cluster,\n\t\t\tGroupName:   r.opts.group,\n\t\t})\n\t\tif e != nil {\n\t\t\treturn fmt.Errorf(\"RegisterInstance err %v,%v\", e, endpoint)\n\t\t}\n\t}\n\treturn nil\n}\n\n// Deregister the registration.\nfunc (r *Registry) Deregister(_ context.Context, service *registry.ServiceInstance) error {\n\tfor _, endpoint := range service.Endpoints {\n\t\tu, err := url.Parse(endpoint)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\thost, port, err := net.SplitHostPort(u.Host)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tp, err := strconv.Atoi(port)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif _, err = r.cli.DeregisterInstance(vo.DeregisterInstanceParam{\n\t\t\tIp:          host,\n\t\t\tPort:        uint64(p),\n\t\t\tServiceName: service.Name + \".\" + u.Scheme,\n\t\t\tGroupName:   r.opts.group,\n\t\t\tCluster:     r.opts.cluster,\n\t\t\tEphemeral:   true,\n\t\t}); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n\n// Watch creates a watcher according to the service name.\nfunc (r *Registry) Watch(ctx context.Context, serviceName string) (registry.Watcher, error) {\n\treturn newWatcher(ctx, r.cli, serviceName, r.opts.group, r.opts.kind, []string{r.opts.cluster})\n}\n\n// GetService return the service instances in memory according to the service name.\nfunc (r *Registry) GetService(_ context.Context, serviceName string) ([]*registry.ServiceInstance, error) {\n\tres, err := r.cli.SelectInstances(vo.SelectInstancesParam{\n\t\tServiceName: serviceName,\n\t\tGroupName:   r.opts.group,\n\t\tHealthyOnly: true,\n\t})\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\titems := make([]*registry.ServiceInstance, 0, len(res))\n\tfor _, in := range res {\n\t\tkind := r.opts.kind\n\t\tweight := r.opts.weight\n\t\tif k, ok := in.Metadata[\"kind\"]; ok {\n\t\t\tkind = k\n\t\t}\n\t\tif in.Weight > 0 {\n\t\t\tweight = in.Weight\n\t\t}\n\n\t\tr := &registry.ServiceInstance{\n\t\t\tID:        in.InstanceId,\n\t\t\tName:      in.ServiceName,\n\t\t\tVersion:   in.Metadata[\"version\"],\n\t\t\tMetadata:  in.Metadata,\n\t\t\tEndpoints: []string{kind + \"://\" + net.JoinHostPort(in.Ip, strconv.Itoa(int(in.Port)))},\n\t\t}\n\t\tr.Metadata[\"weight\"] = strconv.FormatInt(int64(math.Ceil(weight)), 10)\n\t\titems = append(items, r)\n\t}\n\treturn items, nil\n}\n"
  },
  {
    "path": "contrib/registry/nacos/registry_test.go",
    "content": "package nacos\n\nimport (\n\t\"context\"\n\t\"reflect\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/nacos-group/nacos-sdk-go/clients\"\n\t\"github.com/nacos-group/nacos-sdk-go/common/constant\"\n\t\"github.com/nacos-group/nacos-sdk-go/vo\"\n\n\t\"github.com/go-kratos/kratos/v2/registry\"\n)\n\nvar testServerConfig = []constant.ServerConfig{\n\t*constant.NewServerConfig(\"127.0.0.1\", 8848),\n}\n\nfunc TestRegistry_Register(t *testing.T) {\n\tsc := testServerConfig\n\n\tcc := constant.ClientConfig{\n\t\tNamespaceId:         \"public\", // namespace id\n\t\tTimeoutMs:           5000,\n\t\tNotLoadCacheAtStart: true,\n\t\tLogDir:              \"/tmp/nacos/log\",\n\t\tCacheDir:            \"/tmp/nacos/cache\",\n\t\tRotateTime:          \"1h\",\n\t\tMaxAge:              3,\n\t\tLogLevel:            \"debug\",\n\t}\n\n\t// a more graceful way to create naming client\n\tclient, err := clients.NewNamingClient(\n\t\tvo.NacosClientParam{\n\t\t\tClientConfig:  &cc,\n\t\t\tServerConfigs: sc,\n\t\t},\n\t)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tr := New(client)\n\n\ttestServer := &registry.ServiceInstance{\n\t\tID:        \"1\",\n\t\tName:      \"test1\",\n\t\tVersion:   \"v1.0.0\",\n\t\tEndpoints: []string{\"http://127.0.0.1:8080?isSecure=false\"},\n\t}\n\ttestServerWithMetadata := &registry.ServiceInstance{\n\t\tID:        \"1\",\n\t\tName:      \"test1\",\n\t\tVersion:   \"v1.0.0\",\n\t\tEndpoints: []string{\"http://127.0.0.1:8080?isSecure=false\"},\n\t\tMetadata:  map[string]string{\"idc\": \"shanghai-xs\"},\n\t}\n\ttype fields struct {\n\t\tregistry *Registry\n\t}\n\ttype args struct {\n\t\tctx     context.Context\n\t\tservice *registry.ServiceInstance\n\t}\n\ttests := []struct {\n\t\tname      string\n\t\tfields    fields\n\t\targs      args\n\t\twantErr   bool\n\t\tdeferFunc func(t *testing.T)\n\t}{\n\t\t{\n\t\t\tname: \"normal\",\n\t\t\tfields: fields{\n\t\t\t\tregistry: New(client),\n\t\t\t},\n\t\t\targs: args{\n\t\t\t\tctx:     context.Background(),\n\t\t\t\tservice: testServer,\n\t\t\t},\n\t\t\twantErr: false,\n\t\t\tdeferFunc: func(t *testing.T) {\n\t\t\t\terr = r.Deregister(context.Background(), testServer)\n\t\t\t\tif err != nil {\n\t\t\t\t\tt.Error(err)\n\t\t\t\t}\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"withMetadata\",\n\t\t\tfields: fields{\n\t\t\t\tregistry: New(client),\n\t\t\t},\n\t\t\targs: args{\n\t\t\t\tctx:     context.Background(),\n\t\t\t\tservice: testServerWithMetadata,\n\t\t\t},\n\t\t\twantErr: false,\n\t\t\tdeferFunc: func(t *testing.T) {\n\t\t\t\terr = r.Deregister(context.Background(), testServerWithMetadata)\n\t\t\t\tif err != nil {\n\t\t\t\t\tt.Error(err)\n\t\t\t\t}\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"error\",\n\t\t\tfields: fields{\n\t\t\t\tregistry: New(client),\n\t\t\t},\n\t\t\targs: args{\n\t\t\t\tctx: context.Background(),\n\t\t\t\tservice: &registry.ServiceInstance{\n\t\t\t\t\tID:        \"1\",\n\t\t\t\t\tName:      \"\",\n\t\t\t\t\tVersion:   \"v1.0.0\",\n\t\t\t\t\tEndpoints: []string{\"http://127.0.0.1:8080?isSecure=false\"},\n\t\t\t\t},\n\t\t\t},\n\t\t\twantErr: true,\n\t\t},\n\t\t{\n\t\t\tname: \"urlError\",\n\t\t\tfields: fields{\n\t\t\t\tregistry: New(client),\n\t\t\t},\n\t\t\targs: args{\n\t\t\t\tctx: context.Background(),\n\t\t\t\tservice: &registry.ServiceInstance{\n\t\t\t\t\tID:        \"1\",\n\t\t\t\t\tName:      \"test\",\n\t\t\t\t\tVersion:   \"v1.0.0\",\n\t\t\t\t\tEndpoints: []string{\"127.0.0.1:8080\"},\n\t\t\t\t},\n\t\t\t},\n\t\t\twantErr: true,\n\t\t},\n\t\t{\n\t\t\tname: \"portError\",\n\t\t\tfields: fields{\n\t\t\t\tregistry: New(client),\n\t\t\t},\n\t\t\targs: args{\n\t\t\t\tctx: context.Background(),\n\t\t\t\tservice: &registry.ServiceInstance{\n\t\t\t\t\tID:        \"1\",\n\t\t\t\t\tName:      \"test\",\n\t\t\t\t\tVersion:   \"v1.0.0\",\n\t\t\t\t\tEndpoints: []string{\"http://127.0.0.1888\"},\n\t\t\t\t},\n\t\t\t},\n\t\t\twantErr: true,\n\t\t},\n\t\t{\n\t\t\tname: \"withCluster\",\n\t\t\tfields: fields{\n\t\t\t\tregistry: New(client, WithCluster(\"test\")),\n\t\t\t},\n\t\t\targs: args{\n\t\t\t\tctx:     context.Background(),\n\t\t\t\tservice: testServer,\n\t\t\t},\n\t\t\twantErr: false,\n\t\t},\n\t\t{\n\t\t\tname: \"withGroup\",\n\t\t\tfields: fields{\n\t\t\t\tregistry: New(client, WithGroup(\"TEST_GROUP\")),\n\t\t\t},\n\t\t\targs: args{\n\t\t\t\tctx:     context.Background(),\n\t\t\t\tservice: testServer,\n\t\t\t},\n\t\t\twantErr: false,\n\t\t},\n\t\t{\n\t\t\tname: \"withWeight\",\n\t\t\tfields: fields{\n\t\t\t\tregistry: New(client, WithWeight(200)),\n\t\t\t},\n\t\t\targs: args{\n\t\t\t\tctx:     context.Background(),\n\t\t\t\tservice: testServer,\n\t\t\t},\n\t\t\twantErr: false,\n\t\t},\n\t\t{\n\t\t\tname: \"withPrefix\",\n\t\t\tfields: fields{\n\t\t\t\tregistry: New(client, WithPrefix(\"test\")),\n\t\t\t},\n\t\t\targs: args{\n\t\t\t\tctx:     context.Background(),\n\t\t\t\tservice: testServer,\n\t\t\t},\n\t\t\twantErr: false,\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tr := tt.fields.registry\n\t\t\tif err := r.Register(tt.args.ctx, tt.args.service); (err != nil) != tt.wantErr {\n\t\t\t\tt.Errorf(\"Register error = %v, wantErr %v\", err, tt.wantErr)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestRegistry_Deregister(t *testing.T) {\n\ttestServer := &registry.ServiceInstance{\n\t\tID:        \"1\",\n\t\tName:      \"test2\",\n\t\tVersion:   \"v1.0.0\",\n\t\tEndpoints: []string{\"http://127.0.0.1:8080?isSecure=false\"},\n\t}\n\n\ttype args struct {\n\t\tctx     context.Context\n\t\tservice *registry.ServiceInstance\n\t}\n\ttests := []struct {\n\t\tname    string\n\t\targs    args\n\t\twantErr bool\n\t\tpreFunc func(t *testing.T)\n\t}{\n\t\t{\n\t\t\tname: \"normal\",\n\t\t\targs: args{\n\t\t\t\tctx:     context.Background(),\n\t\t\t\tservice: testServer,\n\t\t\t},\n\t\t\twantErr: false,\n\t\t\tpreFunc: func(t *testing.T) {\n\t\t\t\tsc := testServerConfig\n\n\t\t\t\tcc := constant.ClientConfig{\n\t\t\t\t\tNamespaceId:         \"public\", // namespace id\n\t\t\t\t\tTimeoutMs:           5000,\n\t\t\t\t\tNotLoadCacheAtStart: true,\n\t\t\t\t\tLogDir:              \"/tmp/nacos/log\",\n\t\t\t\t\tCacheDir:            \"/tmp/nacos/cache\",\n\t\t\t\t\tRotateTime:          \"1h\",\n\t\t\t\t\tMaxAge:              3,\n\t\t\t\t\tLogLevel:            \"debug\",\n\t\t\t\t}\n\n\t\t\t\t// a more graceful way to create naming client\n\t\t\t\tclient, err := clients.NewNamingClient(\n\t\t\t\t\tvo.NacosClientParam{\n\t\t\t\t\t\tClientConfig:  &cc,\n\t\t\t\t\t\tServerConfigs: sc,\n\t\t\t\t\t},\n\t\t\t\t)\n\t\t\t\tif err != nil {\n\t\t\t\t\tt.Fatal(err)\n\t\t\t\t}\n\t\t\t\tr := New(client)\n\t\t\t\terr = r.Register(context.Background(), testServer)\n\t\t\t\tif err != nil {\n\t\t\t\t\tt.Error(err)\n\t\t\t\t}\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"error\",\n\t\t\targs: args{\n\t\t\t\tctx: context.Background(),\n\t\t\t\tservice: &registry.ServiceInstance{\n\t\t\t\t\tID:        \"1\",\n\t\t\t\t\tName:      \"test\",\n\t\t\t\t\tVersion:   \"v1.0.0\",\n\t\t\t\t\tEndpoints: []string{\"127.0.0.1:8080\"},\n\t\t\t\t},\n\t\t\t},\n\t\t\twantErr: true,\n\t\t},\n\t\t{\n\t\t\tname: \"errorPort\",\n\t\t\targs: args{\n\t\t\t\tctx: context.Background(),\n\t\t\t\tservice: &registry.ServiceInstance{\n\t\t\t\t\tID:        \"1\",\n\t\t\t\t\tName:      \"notExist\",\n\t\t\t\t\tVersion:   \"v1.0.0\",\n\t\t\t\t\tEndpoints: []string{\"http://127.0.0.18080\"},\n\t\t\t\t},\n\t\t\t},\n\t\t\twantErr: true,\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tsc := testServerConfig\n\n\t\t\tcc := constant.ClientConfig{\n\t\t\t\tNamespaceId:         \"public\", // namespace id\n\t\t\t\tTimeoutMs:           5000,\n\t\t\t\tNotLoadCacheAtStart: true,\n\t\t\t\tLogDir:              \"/tmp/nacos/log\",\n\t\t\t\tCacheDir:            \"/tmp/nacos/cache\",\n\t\t\t\tRotateTime:          \"1h\",\n\t\t\t\tMaxAge:              3,\n\t\t\t\tLogLevel:            \"debug\",\n\t\t\t}\n\n\t\t\t// a more graceful way to create naming client\n\t\t\tclient, err := clients.NewNamingClient(\n\t\t\t\tvo.NacosClientParam{\n\t\t\t\t\tClientConfig:  &cc,\n\t\t\t\t\tServerConfigs: sc,\n\t\t\t\t},\n\t\t\t)\n\t\t\tif err != nil {\n\t\t\t\tt.Fatal(err)\n\t\t\t}\n\t\t\tr := New(client)\n\t\t\tif tt.preFunc != nil {\n\t\t\t\ttt.preFunc(t)\n\t\t\t}\n\t\t\tif err := r.Deregister(tt.args.ctx, tt.args.service); (err != nil) != tt.wantErr {\n\t\t\t\tt.Errorf(\"Deregister error = %v, wantErr %v\", err, tt.wantErr)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestRegistry_GetService(t *testing.T) {\n\tsc := testServerConfig\n\n\tcc := constant.ClientConfig{\n\t\tNamespaceId:         \"public\", // namespace id\n\t\tTimeoutMs:           5000,\n\t\tNotLoadCacheAtStart: true,\n\t\tLogDir:              \"/tmp/nacos/log\",\n\t\tCacheDir:            \"/tmp/nacos/cache\",\n\t\tRotateTime:          \"1h\",\n\t\tMaxAge:              3,\n\t\tLogLevel:            \"debug\",\n\t}\n\n\t// a more graceful way to create naming client\n\tclient, err := clients.NewNamingClient(\n\t\tvo.NacosClientParam{\n\t\t\tClientConfig:  &cc,\n\t\t\tServerConfigs: sc,\n\t\t},\n\t)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tr := New(client)\n\ttestServer := &registry.ServiceInstance{\n\t\tID:        \"1\",\n\t\tName:      \"test3\",\n\t\tVersion:   \"v1.0.0\",\n\t\tEndpoints: []string{\"grpc://127.0.0.1:8080?isSecure=false\"},\n\t}\n\n\ttype fields struct {\n\t\tregistry *Registry\n\t}\n\ttype args struct {\n\t\tctx         context.Context\n\t\tserviceName string\n\t}\n\ttests := []struct {\n\t\tname      string\n\t\tfields    fields\n\t\targs      args\n\t\twant      []*registry.ServiceInstance\n\t\twantErr   bool\n\t\tpreFunc   func(t *testing.T)\n\t\tdeferFunc func(t *testing.T)\n\t}{\n\t\t{\n\t\t\tname: \"normal\",\n\t\t\tpreFunc: func(t *testing.T) {\n\t\t\t\terr = r.Register(context.Background(), testServer)\n\t\t\t\tif err != nil {\n\t\t\t\t\tt.Error(err)\n\t\t\t\t}\n\t\t\t\ttime.Sleep(time.Second * 3)\n\t\t\t},\n\t\t\tdeferFunc: func(t *testing.T) {\n\t\t\t\terr = r.Deregister(context.Background(), testServer)\n\t\t\t\tif err != nil {\n\t\t\t\t\tt.Error(err)\n\t\t\t\t}\n\t\t\t},\n\t\t\tfields: fields{\n\t\t\t\tregistry: r,\n\t\t\t},\n\t\t\targs: args{\n\t\t\t\tctx:         context.Background(),\n\t\t\t\tserviceName: testServer.Name + \".\" + \"grpc\",\n\t\t\t},\n\t\t\twant: []*registry.ServiceInstance{{\n\t\t\t\tID:        \"127.0.0.1#8080#DEFAULT#DEFAULT_GROUP@@test3.grpc\",\n\t\t\t\tName:      \"DEFAULT_GROUP@@test3.grpc\",\n\t\t\t\tVersion:   \"v1.0.0\",\n\t\t\t\tMetadata:  map[string]string{\"version\": \"v1.0.0\", \"kind\": \"grpc\", \"weight\": \"100\"},\n\t\t\t\tEndpoints: []string{\"grpc://127.0.0.1:8080\"},\n\t\t\t}},\n\t\t\twantErr: false,\n\t\t},\n\t\t{\n\t\t\tname: \"errorNotExist\",\n\t\t\tfields: fields{\n\t\t\t\tregistry: r,\n\t\t\t},\n\t\t\targs: args{\n\t\t\t\tctx:         context.Background(),\n\t\t\t\tserviceName: \"notExist\",\n\t\t\t},\n\t\t\twant:    nil,\n\t\t\twantErr: true,\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tif tt.preFunc != nil {\n\t\t\t\ttt.preFunc(t)\n\t\t\t}\n\t\t\tif tt.deferFunc != nil {\n\t\t\t\tdefer tt.deferFunc(t)\n\t\t\t}\n\t\t\tr := tt.fields.registry\n\t\t\tgot, err := r.GetService(tt.args.ctx, tt.args.serviceName)\n\t\t\tif (err != nil) != tt.wantErr {\n\t\t\t\tt.Errorf(\"GetService error = %v, wantErr %v\", err, tt.wantErr)\n\t\t\t\tt.Errorf(\"GetService got = %v\", got)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif !reflect.DeepEqual(got, tt.want) {\n\t\t\t\tt.Errorf(\"GetService got = %v, want %v\", got, tt.want)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestRegistry_Watch(t *testing.T) {\n\tsc := testServerConfig\n\n\tcc := constant.ClientConfig{\n\t\tNamespaceId:         \"public\", // namespace id\n\t\tTimeoutMs:           5000,\n\t\tNotLoadCacheAtStart: true,\n\t\tLogDir:              \"/tmp/nacos/log\",\n\t\tCacheDir:            \"/tmp/nacos/cache\",\n\t\tRotateTime:          \"1h\",\n\t\tMaxAge:              3,\n\t\tLogLevel:            \"debug\",\n\t}\n\n\t// a more graceful way to create naming client\n\tclient, err := clients.NewNamingClient(\n\t\tvo.NacosClientParam{\n\t\t\tClientConfig:  &cc,\n\t\t\tServerConfigs: sc,\n\t\t},\n\t)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tr := New(client)\n\n\ttestServer := &registry.ServiceInstance{\n\t\tID:        \"1\",\n\t\tName:      \"test4\",\n\t\tVersion:   \"v1.0.0\",\n\t\tEndpoints: []string{\"grpc://127.0.0.1:8080?isSecure=false\"},\n\t}\n\n\tcancelCtx, cancel := context.WithCancel(context.Background())\n\ttype fields struct {\n\t\tregistry *Registry\n\t}\n\ttype args struct {\n\t\tctx         context.Context\n\t\tserviceName string\n\t}\n\ttests := []struct {\n\t\tname        string\n\t\tfields      fields\n\t\targs        args\n\t\twantErr     bool\n\t\twant        []*registry.ServiceInstance\n\t\tprocessFunc func(t *testing.T)\n\t}{\n\t\t{\n\t\t\tname: \"normal\",\n\t\t\tfields: fields{\n\t\t\t\tregistry: New(client),\n\t\t\t},\n\t\t\targs: args{\n\t\t\t\tctx:         context.Background(),\n\t\t\t\tserviceName: testServer.Name + \".\" + \"grpc\",\n\t\t\t},\n\t\t\twantErr: false,\n\t\t\twant: []*registry.ServiceInstance{{\n\t\t\t\tID:        \"127.0.0.1#8080#DEFAULT#DEFAULT_GROUP@@test4.grpc\",\n\t\t\t\tName:      \"DEFAULT_GROUP@@test4.grpc\",\n\t\t\t\tVersion:   \"v1.0.0\",\n\t\t\t\tMetadata:  map[string]string{\"version\": \"v1.0.0\", \"kind\": \"grpc\"},\n\t\t\t\tEndpoints: []string{\"grpc://127.0.0.1:8080\"},\n\t\t\t}},\n\t\t\tprocessFunc: func(t *testing.T) {\n\t\t\t\terr = r.Register(context.Background(), testServer)\n\t\t\t\tif err != nil {\n\t\t\t\t\tt.Error(err)\n\t\t\t\t}\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"ctxCancel\",\n\t\t\tfields: fields{\n\t\t\t\tregistry: r,\n\t\t\t},\n\t\t\targs: args{\n\t\t\t\tctx:         cancelCtx,\n\t\t\t\tserviceName: testServer.Name,\n\t\t\t},\n\t\t\twantErr: true,\n\t\t\twant:    nil,\n\t\t\tprocessFunc: func(*testing.T) {\n\t\t\t\tcancel()\n\t\t\t},\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tr := tt.fields.registry\n\t\t\twatch, err := r.Watch(tt.args.ctx, tt.args.serviceName)\n\t\t\tif err != nil {\n\t\t\t\tt.Error(err)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tdefer func() {\n\t\t\t\terr = watch.Stop()\n\t\t\t\tif err != nil {\n\t\t\t\t\tt.Error(err)\n\t\t\t\t}\n\t\t\t}()\n\t\t\t_, err = watch.Next()\n\t\t\tif err != nil {\n\t\t\t\tt.Error(err)\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tif tt.processFunc != nil {\n\t\t\t\ttt.processFunc(t)\n\t\t\t}\n\n\t\t\twant, err := watch.Next()\n\t\t\tif (err != nil) != tt.wantErr {\n\t\t\t\tt.Errorf(\"Watch error = %v, wantErr %v\", err, tt.wantErr)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif !reflect.DeepEqual(want, tt.want) {\n\t\t\t\tt.Errorf(\"Watch watcher = %v, want %v\", watch, tt.want)\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "contrib/registry/nacos/watcher.go",
    "content": "package nacos\n\nimport (\n\t\"context\"\n\t\"net\"\n\t\"strconv\"\n\n\t\"github.com/nacos-group/nacos-sdk-go/clients/naming_client\"\n\t\"github.com/nacos-group/nacos-sdk-go/model\"\n\t\"github.com/nacos-group/nacos-sdk-go/vo\"\n\n\t\"github.com/go-kratos/kratos/v2/registry\"\n)\n\nvar _ registry.Watcher = (*watcher)(nil)\n\ntype watcher struct {\n\tserviceName    string\n\tclusters       []string\n\tgroupName      string\n\tctx            context.Context\n\tcancel         context.CancelFunc\n\twatchChan      chan struct{}\n\tcli            naming_client.INamingClient\n\tkind           string\n\tsubscribeParam *vo.SubscribeParam\n}\n\nfunc newWatcher(ctx context.Context, cli naming_client.INamingClient, serviceName, groupName, kind string, clusters []string) (*watcher, error) {\n\tw := &watcher{\n\t\tserviceName: serviceName,\n\t\tclusters:    clusters,\n\t\tgroupName:   groupName,\n\t\tcli:         cli,\n\t\tkind:        kind,\n\t\twatchChan:   make(chan struct{}, 1),\n\t}\n\tw.ctx, w.cancel = context.WithCancel(ctx)\n\n\tw.subscribeParam = &vo.SubscribeParam{\n\t\tServiceName: serviceName,\n\t\tClusters:    clusters,\n\t\tGroupName:   groupName,\n\t\tSubscribeCallback: func([]model.SubscribeService, error) {\n\t\t\tselect {\n\t\t\tcase w.watchChan <- struct{}{}:\n\t\t\tdefault:\n\t\t\t}\n\t\t},\n\t}\n\te := w.cli.Subscribe(w.subscribeParam)\n\tselect {\n\tcase w.watchChan <- struct{}{}:\n\tdefault:\n\t}\n\treturn w, e\n}\n\nfunc (w *watcher) Next() ([]*registry.ServiceInstance, error) {\n\tselect {\n\tcase <-w.ctx.Done():\n\t\treturn nil, w.ctx.Err()\n\tcase <-w.watchChan:\n\t}\n\tres, err := w.cli.GetService(vo.GetServiceParam{\n\t\tServiceName: w.serviceName,\n\t\tGroupName:   w.groupName,\n\t\tClusters:    w.clusters,\n\t})\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\titems := make([]*registry.ServiceInstance, 0, len(res.Hosts))\n\tfor _, in := range res.Hosts {\n\t\tkind := w.kind\n\t\tif k, ok := in.Metadata[\"kind\"]; ok {\n\t\t\tkind = k\n\t\t}\n\t\titems = append(items, &registry.ServiceInstance{\n\t\t\tID:        in.InstanceId,\n\t\t\tName:      res.Name,\n\t\t\tVersion:   in.Metadata[\"version\"],\n\t\t\tMetadata:  in.Metadata,\n\t\t\tEndpoints: []string{kind + \"://\" + net.JoinHostPort(in.Ip, strconv.Itoa(int(in.Port)))},\n\t\t})\n\t}\n\treturn items, nil\n}\n\nfunc (w *watcher) Stop() error {\n\terr := w.cli.Unsubscribe(w.subscribeParam)\n\tw.cancel()\n\treturn err\n}\n"
  },
  {
    "path": "contrib/registry/polaris/go.mod",
    "content": "module github.com/go-kratos/kratos/contrib/registry/polaris/v2\n\ngo 1.22\n\nrequire (\n\tgithub.com/go-kratos/kratos/v2 v2.9.2\n\tgithub.com/polarismesh/polaris-go v1.3.0\n)\n\nrequire (\n\tgithub.com/beorn7/perks v1.0.1 // indirect\n\tgithub.com/cespare/xxhash/v2 v2.2.0 // indirect\n\tgithub.com/dlclark/regexp2 v1.7.0 // indirect\n\tgithub.com/golang/protobuf v1.5.4 // indirect\n\tgithub.com/google/uuid v1.4.0 // indirect\n\tgithub.com/hashicorp/errwrap v1.0.0 // indirect\n\tgithub.com/hashicorp/go-multierror v1.1.1 // indirect\n\tgithub.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect\n\tgithub.com/mitchellh/go-homedir v1.1.0 // indirect\n\tgithub.com/modern-go/reflect2 v1.0.2 // indirect\n\tgithub.com/natefinch/lumberjack v2.0.0+incompatible // indirect\n\tgithub.com/prometheus/client_golang v1.12.1 // indirect\n\tgithub.com/prometheus/client_model v0.2.0 // indirect\n\tgithub.com/prometheus/common v0.32.1 // indirect\n\tgithub.com/prometheus/procfs v0.7.3 // indirect\n\tgithub.com/spaolacci/murmur3 v1.1.0 // indirect\n\tgo.uber.org/atomic v1.7.0 // indirect\n\tgo.uber.org/multierr v1.6.0 // indirect\n\tgo.uber.org/zap v1.21.0 // indirect\n\tgolang.org/x/net v0.33.0 // indirect\n\tgolang.org/x/sys v0.28.0 // indirect\n\tgolang.org/x/text v0.21.0 // indirect\n\tgoogle.golang.org/genproto/googleapis/rpc v0.0.0-20240102182953-50ed04b92917 // indirect\n\tgoogle.golang.org/grpc v1.61.1 // indirect\n\tgoogle.golang.org/protobuf v1.33.0 // indirect\n\tgopkg.in/yaml.v2 v2.4.0 // indirect\n)\n\nreplace github.com/go-kratos/kratos/v2 => ../../../\n"
  },
  {
    "path": "contrib/registry/polaris/go.sum",
    "content": "cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=\ncloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=\ncloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=\ncloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=\ncloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=\ncloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=\ncloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=\ncloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To=\ncloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4=\ncloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M=\ncloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc=\ncloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk=\ncloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs=\ncloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc=\ncloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY=\ncloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=\ncloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=\ncloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=\ncloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=\ncloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=\ncloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=\ncloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=\ncloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=\ncloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=\ncloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=\ncloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=\ncloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU=\ncloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=\ncloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=\ncloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=\ncloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=\ncloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=\ndmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=\ngithub.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=\ngithub.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=\ngithub.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=\ngithub.com/agiledragon/gomonkey v2.0.2+incompatible h1:eXKi9/piiC3cjJD1658mEE2o3NjkJ5vDLgYjCQu0Xlw=\ngithub.com/agiledragon/gomonkey v2.0.2+incompatible/go.mod h1:2NGfXu1a80LLr2cmWXGBDaHEjb1idR6+FVlX5T3D9hw=\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/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=\ngithub.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=\ngithub.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=\ngithub.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=\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 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=\ngithub.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=\ngithub.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=\ngithub.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=\ngithub.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=\ngithub.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=\ngithub.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=\ngithub.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=\ngithub.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=\ngithub.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=\ngithub.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=\ngithub.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=\ngithub.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=\ngithub.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI=\ngithub.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=\ngithub.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=\ngithub.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=\ngithub.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.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/dlclark/regexp2 v1.7.0 h1:7lJfhqlPssTb1WQx4yvTHN0uElPEv52sbaECrAQxjAo=\ngithub.com/dlclark/regexp2 v1.7.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=\ngithub.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=\ngithub.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=\ngithub.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=\ngithub.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=\ngithub.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE=\ngithub.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=\ngithub.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=\ngithub.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=\ngithub.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=\ngithub.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=\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-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=\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-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=\ngithub.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=\ngithub.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=\ngithub.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=\ngithub.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=\ngithub.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=\ngithub.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=\ngithub.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=\ngithub.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=\ngithub.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=\ngithub.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=\ngithub.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=\ngithub.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=\ngithub.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=\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.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=\ngithub.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=\ngithub.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=\ngithub.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=\ngithub.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=\ngithub.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=\ngithub.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=\ngithub.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=\ngithub.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=\ngithub.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=\ngithub.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=\ngithub.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=\ngithub.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=\ngithub.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=\ngithub.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=\ngithub.com/gonum/blas v0.0.0-20181208220705-f22b278b28ac/go.mod h1:P32wAyui1PQ58Oce/KYkOqQv8cVw1zAapXOl+dRFGbc=\ngithub.com/gonum/floats v0.0.0-20181209220543-c233463c7e82/go.mod h1:PxC8OnwL11+aosOB5+iEPoV3picfs8tUpkVd0pDo+Kg=\ngithub.com/gonum/integrate v0.0.0-20181209220457-a422b5c0fdf2/go.mod h1:pDgmNM6seYpwvPos3q+zxlXMsbve6mOIPucUnUOrI7Y=\ngithub.com/gonum/internal v0.0.0-20181124074243-f884aa714029/go.mod h1:Pu4dmpkhSyOzRwuXkOgAvijx4o+4YMUJJo9OvPYMkks=\ngithub.com/gonum/lapack v0.0.0-20181123203213-e4cdc5a0bff9/go.mod h1:XA3DeT6rxh2EAE789SSiSJNqxPaC0aE9J8NTOI0Jo/A=\ngithub.com/gonum/matrix v0.0.0-20181209220409-c518dec07be9/go.mod h1:0EXg4mc1CNP0HCqCz+K4ts155PXIlUywf0wqN+GfPZw=\ngithub.com/gonum/stat v0.0.0-20181125101827-41a0da705a5b/go.mod h1:Z4GIJBJO3Wa4gD4vbwQxXXZ+WHmW6E9ixmNrwvs0iZs=\ngithub.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=\ngithub.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=\ngithub.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=\ngithub.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=\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.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\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/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=\ngithub.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=\ngithub.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=\ngithub.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=\ngithub.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=\ngithub.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=\ngithub.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=\ngithub.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=\ngithub.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=\ngithub.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=\ngithub.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=\ngithub.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=\ngithub.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=\ngithub.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4=\ngithub.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=\ngithub.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=\ngithub.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=\ngithub.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=\ngithub.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=\ngithub.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=\ngithub.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA=\ngithub.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=\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/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=\ngithub.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=\ngithub.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=\ngithub.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=\ngithub.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=\ngithub.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=\ngithub.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=\ngithub.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=\ngithub.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=\ngithub.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=\ngithub.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=\ngithub.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=\ngithub.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=\ngithub.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=\ngithub.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=\ngithub.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=\ngithub.com/konsorten/go-windows-terminal-sequences v1.0.3/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 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=\ngithub.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=\ngithub.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=\ngithub.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=\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/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=\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/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=\ngithub.com/natefinch/lumberjack v2.0.0+incompatible h1:4QJd3OLAMgj7ph+yZTuX13Ld4UpgHp07nNdFX7mqFfM=\ngithub.com/natefinch/lumberjack v2.0.0+incompatible/go.mod h1:Wi9p2TTF5DG5oU+6YfsmYQpsTIOm0B1VNzQg9Mw6nPk=\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 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=\ngithub.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=\ngithub.com/polarismesh/polaris-go v1.3.0 h1:KZKX//ow4OPPoS5+s7h07ptprg+2AcNVGrN6WakC9QM=\ngithub.com/polarismesh/polaris-go v1.3.0/go.mod h1:HsN0ierETIujHpmnnYJ3qkwQw4QGAECuHvBZTDaw1tI=\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.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=\ngithub.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0=\ngithub.com/prometheus/client_golang v1.12.1 h1:ZiaPsmm9uiBeaSMRznKsCDNtPCS0T3JVDGF+06gjBzk=\ngithub.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY=\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.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=\ngithub.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M=\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.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=\ngithub.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc=\ngithub.com/prometheus/common v0.32.1 h1:hWIdL3N2HoUx3B8j3YN9mWor0qhY/NlEKZEaXxuIRh4=\ngithub.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls=\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.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=\ngithub.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=\ngithub.com/prometheus/procfs v0.7.3 h1:4jVXhlkAyzOScmCkXBTOLRLTz8EeU+eyjrwB/EPq0VU=\ngithub.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=\ngithub.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=\ngithub.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=\ngithub.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=\ngithub.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=\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/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=\ngithub.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=\ngithub.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=\ngithub.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=\ngithub.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=\ngithub.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI=\ngithub.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=\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/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.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=\ngithub.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=\ngithub.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngithub.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=\ngithub.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=\ngithub.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=\ngithub.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=\ngo.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=\ngo.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=\ngo.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=\ngo.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=\ngo.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=\ngo.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=\ngo.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw=\ngo.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=\ngo.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI=\ngo.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=\ngo.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4=\ngo.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=\ngo.uber.org/zap v1.21.0 h1:WefMeulhovoZ2sYXz7st6K0sLj7bBhpiFaud4r4zST8=\ngo.uber.org/zap v1.21.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw=\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-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\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/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=\ngolang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=\ngolang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=\ngolang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=\ngolang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=\ngolang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=\ngolang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=\ngolang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=\ngolang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=\ngolang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=\ngolang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=\ngolang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=\ngolang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=\ngolang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=\ngolang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=\ngolang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=\ngolang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=\ngolang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=\ngolang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=\ngolang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=\ngolang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=\ngolang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=\ngolang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=\ngolang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=\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/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=\ngolang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=\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-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20200222125558-5a598a2470a0/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-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=\ngolang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=\ngolang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=\ngolang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=\ngolang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=\ngolang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=\ngolang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=\ngolang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=\ngolang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=\ngolang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=\ngolang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=\ngolang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=\ngolang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I=\ngolang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=\ngolang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=\ngolang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=\ngolang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=\ngolang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=\ngolang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=\ngolang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=\ngolang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\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-20190227155943-e225da77a7e6/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-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20201207232520-09787c993a3a/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.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=\ngolang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=\ngolang.org/x/sys v0.0.0-20180830151530-49385e6e1522/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-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\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-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200803210538-64077c9b5642/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-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=\ngolang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=\ngolang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=\ngolang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=\ngolang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/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.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\ngolang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\ngolang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=\ngolang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=\ngolang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=\ngolang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=\ngolang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=\ngolang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=\ngolang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=\ngolang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=\ngolang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=\ngolang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=\ngolang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=\ngolang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=\ngolang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=\ngolang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=\ngolang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=\ngolang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=\ngolang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=\ngolang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/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-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=\ngolang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=\ngolang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=\ngolang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=\ngolang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=\ngolang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=\ngolang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=\ngolang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=\ngolang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=\ngolang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=\ngolang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=\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=\ngoogle.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=\ngoogle.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=\ngoogle.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=\ngoogle.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=\ngoogle.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=\ngoogle.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=\ngoogle.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=\ngoogle.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=\ngoogle.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=\ngoogle.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=\ngoogle.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=\ngoogle.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=\ngoogle.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=\ngoogle.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=\ngoogle.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM=\ngoogle.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc=\ngoogle.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=\ngoogle.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=\ngoogle.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=\ngoogle.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=\ngoogle.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=\ngoogle.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=\ngoogle.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=\ngoogle.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=\ngoogle.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=\ngoogle.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=\ngoogle.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=\ngoogle.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=\ngoogle.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=\ngoogle.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=\ngoogle.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=\ngoogle.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=\ngoogle.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=\ngoogle.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=\ngoogle.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=\ngoogle.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=\ngoogle.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA=\ngoogle.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U=\ngoogle.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=\ngoogle.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=\ngoogle.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=\ngoogle.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=\ngoogle.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=\ngoogle.golang.org/genproto v0.0.0-20220504150022-98cd25cafc72/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4=\ngoogle.golang.org/genproto/googleapis/rpc v0.0.0-20240102182953-50ed04b92917 h1:6G8oQ016D88m1xAKljMlBOOGWDZkes4kMhgGFlf8WcQ=\ngoogle.golang.org/genproto/googleapis/rpc v0.0.0-20240102182953-50ed04b92917/go.mod h1:xtjpI3tXFPP051KaWnhvxkiubL/6dJ18vLVf7q2pTOU=\ngoogle.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=\ngoogle.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=\ngoogle.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=\ngoogle.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=\ngoogle.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=\ngoogle.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=\ngoogle.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=\ngoogle.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=\ngoogle.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60=\ngoogle.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=\ngoogle.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=\ngoogle.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=\ngoogle.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=\ngoogle.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=\ngoogle.golang.org/grpc v1.46.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=\ngoogle.golang.org/grpc v1.46.2/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=\ngoogle.golang.org/grpc v1.61.1 h1:kLAiWrZs7YeDM6MumDe7m3y4aM6wacLzM1Y/wiLP9XY=\ngoogle.golang.org/grpc v1.61.1/go.mod h1:VUbo7IFqmF1QtCAstipjG0GIoq49KvMe9+h1jFLBNJs=\ngoogle.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=\ngoogle.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=\ngoogle.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=\ngoogle.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=\ngoogle.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=\ngoogle.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=\ngoogle.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=\ngoogle.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=\ngoogle.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=\ngoogle.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=\ngoogle.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=\ngoogle.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=\ngoogle.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=\ngoogle.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=\ngoogle.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=\ngoogle.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=\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-20180628173108-788fd7840127/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/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=\ngopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8=\ngopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=\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.3/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.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=\ngopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=\ngopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\ngopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\ngopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=\ngopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\nhonnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\nhonnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\nhonnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\nhonnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\nhonnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=\nhonnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=\nhonnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=\nrsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=\nrsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=\nrsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=\n"
  },
  {
    "path": "contrib/registry/polaris/registry.go",
    "content": "package polaris\n\nimport (\n\t\"context\"\n\t\"net\"\n\t\"net/url\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/go-kratos/kratos/v2/log\"\n\n\t\"github.com/go-kratos/kratos/v2/registry\"\n\n\t\"github.com/polarismesh/polaris-go/api\"\n\t\"github.com/polarismesh/polaris-go/pkg/config\"\n\t\"github.com/polarismesh/polaris-go/pkg/model\"\n)\n\nvar (\n\t_ registry.Registrar = (*Registry)(nil)\n\t_ registry.Discovery = (*Registry)(nil)\n)\n\n// _instanceIDSeparator . Instance id Separator.\nconst _instanceIDSeparator = \"-\"\n\ntype options struct {\n\t// required, namespace in polaris\n\tNamespace string\n\n\t// required, service access token\n\tServiceToken string\n\n\t// optional, protocol in polaris. Default value is nil, it means use protocol config in service\n\tProtocol *string\n\n\t// service weight in polaris. Default value is 100, 0 <= weight <= 10000\n\tWeight int\n\n\t// service priority. Default value is 0. The smaller the value, the lower the priority\n\tPriority int\n\n\t// To show service is healthy or not. Default value is True .\n\tHealthy bool\n\n\t// Heartbeat enable .Not in polaris . Default value is True.\n\tHeartbeat bool\n\n\t// To show service is isolate or not. Default value is False .\n\tIsolate bool\n\n\t// TTL timeout. if node needs to use heartbeat to report,required. If not set,server will throw ErrorCode-400141\n\tTTL int\n\n\t// optional, Timeout for single query. Default value is global config\n\t// Total is (1+RetryCount) * Timeout\n\tTimeout time.Duration\n\n\t// optional, retry count. Default value is global config\n\tRetryCount int\n}\n\n// Option is polaris option.\ntype Option func(o *options)\n\n// Registry is polaris registry.\ntype Registry struct {\n\topt      options\n\tprovider api.ProviderAPI\n\tconsumer api.ConsumerAPI\n}\n\n// WithNamespace with Namespace option.\nfunc WithNamespace(namespace string) Option {\n\treturn func(o *options) { o.Namespace = namespace }\n}\n\n// WithServiceToken with ServiceToken option.\nfunc WithServiceToken(serviceToken string) Option {\n\treturn func(o *options) { o.ServiceToken = serviceToken }\n}\n\n// WithProtocol with Protocol option.\nfunc WithProtocol(protocol string) Option {\n\treturn func(o *options) { o.Protocol = &protocol }\n}\n\n// WithWeight with Weight option.\nfunc WithWeight(weight int) Option {\n\treturn func(o *options) { o.Weight = weight }\n}\n\n// WithHealthy with Healthy option.\nfunc WithHealthy(healthy bool) Option {\n\treturn func(o *options) { o.Healthy = healthy }\n}\n\n// WithIsolate with Isolate option.\nfunc WithIsolate(isolate bool) Option {\n\treturn func(o *options) { o.Isolate = isolate }\n}\n\n// WithTTL with TTL option.\nfunc WithTTL(TTL int) Option {\n\treturn func(o *options) { o.TTL = TTL }\n}\n\n// WithTimeout with Timeout option.\nfunc WithTimeout(timeout time.Duration) Option {\n\treturn func(o *options) { o.Timeout = timeout }\n}\n\n// WithRetryCount with RetryCount option.\nfunc WithRetryCount(retryCount int) Option {\n\treturn func(o *options) { o.RetryCount = retryCount }\n}\n\n// WithHeartbeat . with Heartbeat option.\nfunc WithHeartbeat(heartbeat bool) Option {\n\treturn func(o *options) { o.Heartbeat = heartbeat }\n}\n\nfunc NewRegistry(provider api.ProviderAPI, consumer api.ConsumerAPI, opts ...Option) (r *Registry) {\n\top := options{\n\t\tNamespace:    \"default\",\n\t\tServiceToken: \"\",\n\t\tProtocol:     nil,\n\t\tWeight:       0,\n\t\tPriority:     0,\n\t\tHealthy:      true,\n\t\tHeartbeat:    true,\n\t\tIsolate:      false,\n\t\tTTL:          0,\n\t\tTimeout:      0,\n\t\tRetryCount:   0,\n\t}\n\tfor _, option := range opts {\n\t\toption(&op)\n\t}\n\treturn &Registry{\n\t\topt:      op,\n\t\tprovider: provider,\n\t\tconsumer: consumer,\n\t}\n}\n\nfunc NewRegistryWithConfig(conf config.Configuration, opts ...Option) (r *Registry) {\n\tprovider, err := api.NewProviderAPIByConfig(conf)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tconsumer, err := api.NewConsumerAPIByConfig(conf)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\treturn NewRegistry(provider, consumer, opts...)\n}\n\n// Register the registration.\nfunc (r *Registry) Register(_ context.Context, serviceInstance *registry.ServiceInstance) error {\n\tids := make([]string, 0, len(serviceInstance.Endpoints))\n\tfor _, endpoint := range serviceInstance.Endpoints {\n\t\t// get url\n\t\tu, err := url.Parse(endpoint)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\t// get host and port\n\t\thost, port, err := net.SplitHostPort(u.Host)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\t// port to int\n\t\tportNum, err := strconv.Atoi(port)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\t// medata\n\t\tvar rmd map[string]string\n\t\tif serviceInstance.Metadata == nil {\n\t\t\trmd = map[string]string{\n\t\t\t\t\"kind\":    u.Scheme,\n\t\t\t\t\"version\": serviceInstance.Version,\n\t\t\t}\n\t\t} else {\n\t\t\trmd = make(map[string]string, len(serviceInstance.Metadata)+2)\n\t\t\tfor k, v := range serviceInstance.Metadata {\n\t\t\t\trmd[k] = v\n\t\t\t}\n\t\t\trmd[\"kind\"] = u.Scheme\n\t\t\trmd[\"version\"] = serviceInstance.Version\n\t\t}\n\t\t// Register\n\t\tservice, err := r.provider.Register(\n\t\t\t&api.InstanceRegisterRequest{\n\t\t\t\tInstanceRegisterRequest: model.InstanceRegisterRequest{\n\t\t\t\t\tService:      serviceInstance.Name + u.Scheme,\n\t\t\t\t\tServiceToken: r.opt.ServiceToken,\n\t\t\t\t\tNamespace:    r.opt.Namespace,\n\t\t\t\t\tHost:         host,\n\t\t\t\t\tPort:         portNum,\n\t\t\t\t\tProtocol:     r.opt.Protocol,\n\t\t\t\t\tWeight:       &r.opt.Weight,\n\t\t\t\t\tPriority:     &r.opt.Priority,\n\t\t\t\t\tVersion:      &serviceInstance.Version,\n\t\t\t\t\tMetadata:     rmd,\n\t\t\t\t\tHealthy:      &r.opt.Healthy,\n\t\t\t\t\tIsolate:      &r.opt.Isolate,\n\t\t\t\t\tTTL:          &r.opt.TTL,\n\t\t\t\t\tTimeout:      &r.opt.Timeout,\n\t\t\t\t\tRetryCount:   &r.opt.RetryCount,\n\t\t\t\t},\n\t\t\t})\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tinstanceID := service.InstanceID\n\n\t\tif r.opt.Heartbeat {\n\t\t\t// start heartbeat report\n\t\t\tgo func() {\n\t\t\t\tticker := time.NewTicker(time.Second * time.Duration(r.opt.TTL))\n\t\t\t\tdefer ticker.Stop()\n\n\t\t\t\tfor {\n\t\t\t\t\t<-ticker.C\n\n\t\t\t\t\terr = r.provider.Heartbeat(&api.InstanceHeartbeatRequest{\n\t\t\t\t\t\tInstanceHeartbeatRequest: model.InstanceHeartbeatRequest{\n\t\t\t\t\t\t\tService:      serviceInstance.Name + u.Scheme,\n\t\t\t\t\t\t\tNamespace:    r.opt.Namespace,\n\t\t\t\t\t\t\tHost:         host,\n\t\t\t\t\t\t\tPort:         portNum,\n\t\t\t\t\t\t\tServiceToken: r.opt.ServiceToken,\n\t\t\t\t\t\t\tInstanceID:   instanceID,\n\t\t\t\t\t\t\tTimeout:      &r.opt.Timeout,\n\t\t\t\t\t\t\tRetryCount:   &r.opt.RetryCount,\n\t\t\t\t\t\t},\n\t\t\t\t\t})\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\tlog.Error(err.Error())\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}()\n\t\t}\n\n\t\tids = append(ids, instanceID)\n\t}\n\t// need to set InstanceID for Deregister\n\tserviceInstance.ID = strings.Join(ids, _instanceIDSeparator)\n\treturn nil\n}\n\n// Deregister the registration.\nfunc (r *Registry) Deregister(_ context.Context, serviceInstance *registry.ServiceInstance) error {\n\tsplit := strings.Split(serviceInstance.ID, _instanceIDSeparator)\n\tfor i, endpoint := range serviceInstance.Endpoints {\n\t\t// get url\n\t\tu, err := url.Parse(endpoint)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\t// get host and port\n\t\thost, port, err := net.SplitHostPort(u.Host)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\t// port to int\n\t\tportNum, err := strconv.Atoi(port)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\t// Deregister\n\t\terr = r.provider.Deregister(\n\t\t\t&api.InstanceDeRegisterRequest{\n\t\t\t\tInstanceDeRegisterRequest: model.InstanceDeRegisterRequest{\n\t\t\t\t\tService:      serviceInstance.Name + u.Scheme,\n\t\t\t\t\tServiceToken: r.opt.ServiceToken,\n\t\t\t\t\tNamespace:    r.opt.Namespace,\n\t\t\t\t\tInstanceID:   split[i],\n\t\t\t\t\tHost:         host,\n\t\t\t\t\tPort:         portNum,\n\t\t\t\t\tTimeout:      &r.opt.Timeout,\n\t\t\t\t\tRetryCount:   &r.opt.RetryCount,\n\t\t\t\t},\n\t\t\t},\n\t\t)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n\n// GetService return the service instances in memory according to the service name.\nfunc (r *Registry) GetService(_ context.Context, serviceName string) ([]*registry.ServiceInstance, error) {\n\t// get all instances\n\tinstancesResponse, err := r.consumer.GetAllInstances(&api.GetAllInstancesRequest{\n\t\tGetAllInstancesRequest: model.GetAllInstancesRequest{\n\t\t\tService:    serviceName,\n\t\t\tNamespace:  r.opt.Namespace,\n\t\t\tTimeout:    &r.opt.Timeout,\n\t\t\tRetryCount: &r.opt.RetryCount,\n\t\t},\n\t})\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tserviceInstances := instancesToServiceInstances(instancesResponse.GetInstances())\n\n\treturn serviceInstances, nil\n}\n\n// Watch creates a watcher according to the service name.\nfunc (r *Registry) Watch(ctx context.Context, serviceName string) (registry.Watcher, error) {\n\treturn newWatcher(ctx, r.opt.Namespace, serviceName, r.consumer)\n}\n\ntype Watcher struct {\n\tServiceName      string\n\tNamespace        string\n\tCtx              context.Context\n\tCancel           context.CancelFunc\n\tChannel          <-chan model.SubScribeEvent\n\tServiceInstances []*registry.ServiceInstance\n\tfirst            bool\n}\n\nfunc newWatcher(ctx context.Context, namespace string, serviceName string, consumer api.ConsumerAPI) (*Watcher, error) {\n\twatchServiceResponse, err := consumer.WatchService(&api.WatchServiceRequest{\n\t\tWatchServiceRequest: model.WatchServiceRequest{\n\t\t\tKey: model.ServiceKey{\n\t\t\t\tNamespace: namespace,\n\t\t\t\tService:   serviceName,\n\t\t\t},\n\t\t},\n\t})\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tw := &Watcher{\n\t\tNamespace:        namespace,\n\t\tServiceName:      serviceName,\n\t\tfirst:            true,\n\t\tChannel:          watchServiceResponse.EventChannel,\n\t\tServiceInstances: instancesToServiceInstances(watchServiceResponse.GetAllInstancesResp.GetInstances()),\n\t}\n\tw.Ctx, w.Cancel = context.WithCancel(ctx)\n\treturn w, nil\n}\n\n// Next returns services in the following two cases:\n// 1.the first time to watch and the service instance list is not empty.\n// 2.any service instance changes found.\n// if the above two conditions are not met, it will block until context deadline exceeded or canceled\nfunc (w *Watcher) Next() ([]*registry.ServiceInstance, error) {\n\tif w.first {\n\t\tw.first = false\n\t\treturn w.ServiceInstances, nil\n\t}\n\tselect {\n\tcase <-w.Ctx.Done():\n\t\treturn nil, w.Ctx.Err()\n\tcase event := <-w.Channel:\n\t\tif event.GetSubScribeEventType() == model.EventInstance {\n\t\t\t// this always true, but we need to check it to make sure EventType not change\n\t\t\tif instanceEvent, ok := event.(*model.InstanceEvent); ok {\n\t\t\t\t// handle DeleteEvent\n\t\t\t\tif instanceEvent.DeleteEvent != nil {\n\t\t\t\t\tfor _, instance := range instanceEvent.DeleteEvent.Instances {\n\t\t\t\t\t\tfor i, serviceInstance := range w.ServiceInstances {\n\t\t\t\t\t\t\tif serviceInstance.ID == instance.GetId() {\n\t\t\t\t\t\t\t\t// remove equal\n\t\t\t\t\t\t\t\tif len(w.ServiceInstances) <= 1 {\n\t\t\t\t\t\t\t\t\tw.ServiceInstances = w.ServiceInstances[:0]\n\t\t\t\t\t\t\t\t\tcontinue\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tw.ServiceInstances = append(w.ServiceInstances[:i], w.ServiceInstances[i+1:]...)\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t// handle UpdateEvent\n\t\t\t\tif instanceEvent.UpdateEvent != nil {\n\t\t\t\t\tfor i, serviceInstance := range w.ServiceInstances {\n\t\t\t\t\t\tfor _, update := range instanceEvent.UpdateEvent.UpdateList {\n\t\t\t\t\t\t\tif serviceInstance.ID == update.Before.GetId() {\n\t\t\t\t\t\t\t\tw.ServiceInstances[i] = instanceToServiceInstance(update.After)\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t// handle AddEvent\n\t\t\t\tif instanceEvent.AddEvent != nil {\n\t\t\t\t\tw.ServiceInstances = append(w.ServiceInstances, instancesToServiceInstances(instanceEvent.AddEvent.Instances)...)\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn w.ServiceInstances, nil\n\t\t}\n\t}\n\treturn w.ServiceInstances, nil\n}\n\n// Stop close the watcher.\nfunc (w *Watcher) Stop() error {\n\tw.Cancel()\n\treturn nil\n}\n\nfunc instancesToServiceInstances(instances []model.Instance) []*registry.ServiceInstance {\n\tserviceInstances := make([]*registry.ServiceInstance, 0, len(instances))\n\tfor _, instance := range instances {\n\t\tif instance.IsHealthy() {\n\t\t\tserviceInstances = append(serviceInstances, instanceToServiceInstance(instance))\n\t\t}\n\t}\n\treturn serviceInstances\n}\n\nfunc instanceToServiceInstance(instance model.Instance) *registry.ServiceInstance {\n\tmetadata := instance.GetMetadata()\n\t// Usually, it won't fail in kratos if register correctly\n\tkind := \"\"\n\tif k, ok := metadata[\"kind\"]; ok {\n\t\tkind = k\n\t}\n\treturn &registry.ServiceInstance{\n\t\tID:        instance.GetId(),\n\t\tName:      instance.GetService(),\n\t\tVersion:   metadata[\"version\"],\n\t\tMetadata:  metadata,\n\t\tEndpoints: []string{kind + \"://\" + net.JoinHostPort(instance.GetHost(), strconv.Itoa(int(instance.GetPort())))},\n\t}\n}\n"
  },
  {
    "path": "contrib/registry/servicecomb/README.md",
    "content": "# Servicecomb Registry\n\n## example\n\n### server\n```go\npackage main\n\nimport (\n\t\"log\"\n\n\t\"github.com/go-chassis/sc-client\"\n\t\"github.com/go-kratos/kratos/contrib/registry/servicecomb/v2\"\n\t\"github.com/go-kratos/kratos/v2\"\n)\n\nfunc main() {\n\tc, err := sc.NewClient(sc.Options{\n\t\tEndpoints: []string{\"127.0.0.1:30100\"},\n\t})\n\tif err != nil {\n\t\tlog.Panic(err)\n\t}\n\tr := servicecomb.NewRegistry(c)\n\tapp := kratos.New(\n\t\tkratos.Name(\"helloServicecomb\"),\n\t\tkratos.Registrar(r),\n\t)\n\tif err := app.Run(); err != nil {\n\t\tlog.Fatal(err)\n\t}\n}\n```\n\n### client\n```go\npackage main\n\nimport (\n\t\"context\"\n\t\"log\"\n\n\t\"github.com/go-chassis/sc-client\"\n\t\"github.com/go-kratos/kratos/contrib/registry/servicecomb/v2\"\n\t\"github.com/go-kratos/kratos/v2/transport/grpc\"\n)\n\nfunc main() {\n\tc, err := sc.NewClient(sc.Options{\n\t\tEndpoints: []string{\"127.0.0.1:30100\"},\n\t})\n\tif err != nil {\n\t\tlog.Panic(err)\n\t}\n\tr := servicecomb.NewRegistry(c)\n\tctx := context.Background()\n\tconn, err := grpc.DialInsecure(\n\t\tctx,\n\t\tgrpc.WithEndpoint(\"discovery:///helloServicecomb\"),\n\t\tgrpc.WithDiscovery(r),\n\t)\n\tif err != nil {\n\t\treturn\n\t}\n\tdefer conn.Close()\n}\n```\n"
  },
  {
    "path": "contrib/registry/servicecomb/go.mod",
    "content": "module github.com/go-kratos/kratos/contrib/registry/servicecomb/v2\n\ngo 1.22\n\nrequire (\n\tgithub.com/go-chassis/cari v0.6.0\n\tgithub.com/go-chassis/sc-client v0.6.1-0.20210615014358-a45e9090c751\n\tgithub.com/go-kratos/kratos/v2 v2.9.2\n\tgithub.com/gofrs/uuid v4.2.0+incompatible\n)\n\nrequire (\n\tgithub.com/cenkalti/backoff v2.0.0+incompatible // indirect\n\tgithub.com/go-chassis/foundation v0.4.0 // indirect\n\tgithub.com/go-chassis/openlog v1.1.3 // indirect\n\tgithub.com/gogo/protobuf v1.3.2 // indirect\n\tgithub.com/gorilla/websocket v1.4.3-0.20210424162022-e8629af678b7 // indirect\n\tgolang.org/x/net v0.33.0 // indirect\n)\n\nreplace github.com/go-kratos/kratos/v2 => ../../../\n"
  },
  {
    "path": "contrib/registry/servicecomb/go.sum",
    "content": "github.com/cenkalti/backoff v2.0.0+incompatible h1:5IIPUHhlnUZbcHQsQou5k1Tn58nJkeJL9U+ig5CHJbY=\ngithub.com/cenkalti/backoff v2.0.0+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=\ngithub.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.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/deckarep/golang-set v1.7.1/go.mod h1:93vsz/8Wt4joVM7c2AVqh+YRMiUSc14yDtF28KmMOgQ=\ngithub.com/go-chassis/cari v0.4.0/go.mod h1:av/19fqwEP4eOC8unL/z67AAbFDwXUCko6SKa4Avrd8=\ngithub.com/go-chassis/cari v0.6.0 h1:cwBchwt9L8JOyO6QkzXFAsseMJ10zVSiVK8eDLD0HkA=\ngithub.com/go-chassis/cari v0.6.0/go.mod h1:mSDRCOQXGmlD69A6NG0hsv0UP1xbVPtL6HCGI6X1tqs=\ngithub.com/go-chassis/foundation v0.3.0/go.mod h1:2PjwqpVwYEVaAldl5A58a08viH8p27pNeYaiE3ZxOBA=\ngithub.com/go-chassis/foundation v0.4.0 h1:z0xETnSxF+vRXWjoIhOdzt6rywjZ4sB++utEl4YgWEY=\ngithub.com/go-chassis/foundation v0.4.0/go.mod h1:6NsIUaHghTFRGfCBcZN011zl196F6OR5QvD9N+P4oWU=\ngithub.com/go-chassis/openlog v1.1.2/go.mod h1:+eYCADVxWyJkwsFMUBrMxyQlNqW+UUsCxvR2LrYZUaA=\ngithub.com/go-chassis/openlog v1.1.3 h1:XqIOvZ8YPJ9o9lLtLBskQNNWolK5kC6a4Sv7r4s9sZ4=\ngithub.com/go-chassis/openlog v1.1.3/go.mod h1:+eYCADVxWyJkwsFMUBrMxyQlNqW+UUsCxvR2LrYZUaA=\ngithub.com/go-chassis/sc-client v0.6.1-0.20210615014358-a45e9090c751 h1:hpWN/MZBMsnJqXdMkW7v0wsC+4rYulPsBFMrHCmZMQc=\ngithub.com/go-chassis/sc-client v0.6.1-0.20210615014358-a45e9090c751/go.mod h1:TBS9g7OaIeu1OR/9tVPJEl6BgHFcYEdbuJlgVDQczbc=\ngithub.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=\ngithub.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=\ngithub.com/go-playground/validator v9.31.0+incompatible/go.mod h1:yrEkQXlcI+PugkyDjY2bRrL/UBU4f3rvrgkN3V8JEig=\ngithub.com/gofrs/uuid v4.2.0+incompatible h1:yyYWMnhkhrKwwr8gAOcOCYxOOscHgDS9yZgBrnJfGa0=\ngithub.com/gofrs/uuid v4.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=\ngithub.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=\ngithub.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=\ngithub.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=\ngithub.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=\ngithub.com/gorilla/websocket v1.4.3-0.20210424162022-e8629af678b7 h1:L89uC9ATI61/V2eNgZYtQHyjjyjEplemB+aky4HdyzQ=\ngithub.com/gorilla/websocket v1.4.3-0.20210424162022-e8629af678b7/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=\ngithub.com/karlseguin/ccache/v2 v2.0.8/go.mod h1:2BDThcfQMf/c0jnZowt16eW405XIqZPavt+HoYEtcxQ=\ngithub.com/karlseguin/expect v1.0.2-0.20190806010014-778a5f0c6003/go.mod h1:zNBxMY8P21owkeogJELCLeHIt+voOSduHYTFUbwRAV8=\ngithub.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=\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/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY=\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/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\ngithub.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngithub.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=\ngithub.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngithub.com/wsxiaoys/terminal v0.0.0-20160513160801-0940f3fc43a0/go.mod h1:IXCdmsXIht47RaVFLEdVnh1t+pgYtTAhQGj73kz+2DM=\ngithub.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=\ngithub.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=\ngolang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=\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/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-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190620200207-3b0461eec859/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-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=\ngolang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I=\ngolang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=\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.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=\ngolang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=\ngolang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/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-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\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/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\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=\ngopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\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": "contrib/registry/servicecomb/registry.go",
    "content": "package servicecomb\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"os\"\n\t\"time\"\n\n\t\"github.com/go-chassis/cari/discovery\"\n\t\"github.com/go-chassis/cari/pkg/errsvc\"\n\t\"github.com/go-chassis/sc-client\"\n\t\"github.com/gofrs/uuid\"\n\n\t\"github.com/go-kratos/kratos/v2/log\"\n\t\"github.com/go-kratos/kratos/v2/registry\"\n)\n\nfunc init() {\n\tappID = os.Getenv(appIDVar)\n\tif appID == \"\" {\n\t\tappID = \"default\"\n\t}\n\tenv = os.Getenv(envVar)\n}\n\nvar (\n\t_ registry.Registrar = (*Registry)(nil)\n\t_ registry.Discovery = (*Registry)(nil)\n)\n\nvar (\n\tcurServiceID string\n\tappID        string\n\tenv          string\n)\n\nconst (\n\tappIDKey         = \"appId\"\n\tenvKey           = \"environment\"\n\tenvVar           = \"CAS_ENVIRONMENT_ID\"\n\tappIDVar         = \"CAS_APPLICATION_NAME\"\n\tframeWorkName    = \"kratos\"\n\tframeWorkVersion = \"v2\"\n)\n\ntype RegistryClient interface {\n\tGetMicroServiceID(appID, microServiceName, version, env string, opts ...sc.CallOption) (string, error)\n\tFindMicroServiceInstances(consumerID, appID, microServiceName, versionRule string, opts ...sc.CallOption) ([]*discovery.MicroServiceInstance, error)\n\tRegisterService(microService *discovery.MicroService) (string, error)\n\tRegisterMicroServiceInstance(microServiceInstance *discovery.MicroServiceInstance) (string, error)\n\tHeartbeat(microServiceID, microServiceInstanceID string) (bool, error)\n\tUnregisterMicroServiceInstance(microServiceID, microServiceInstanceID string) (bool, error)\n\tWatchMicroService(microServiceID string, callback func(*sc.MicroServiceInstanceChangedEvent)) error\n}\n\n// Registry is servicecomb registry.\ntype Registry struct {\n\tcli RegistryClient\n}\n\nfunc NewRegistry(client RegistryClient) *Registry {\n\tr := &Registry{\n\t\tcli: client,\n\t}\n\treturn r\n}\n\nfunc (r *Registry) GetService(_ context.Context, serviceName string) ([]*registry.ServiceInstance, error) {\n\tinstances, err := r.cli.FindMicroServiceInstances(\"\", appID, serviceName, \"\")\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tsvcInstances := make([]*registry.ServiceInstance, 0, len(instances))\n\tfor _, instance := range instances {\n\t\tsvcInstances = append(svcInstances, &registry.ServiceInstance{\n\t\t\tID:        instance.InstanceId,\n\t\t\tName:      serviceName,\n\t\t\tMetadata:  instance.Properties,\n\t\t\tEndpoints: instance.Endpoints,\n\t\t\tVersion:   instance.ServiceId,\n\t\t})\n\t}\n\treturn svcInstances, nil\n}\n\nfunc (r *Registry) Watch(ctx context.Context, serviceName string) (registry.Watcher, error) {\n\treturn newWatcher(ctx, r.cli, serviceName)\n}\n\nfunc (r *Registry) Register(_ context.Context, svcIns *registry.ServiceInstance) error {\n\tfw := &discovery.FrameWork{\n\t\tName:    frameWorkName,\n\t\tVersion: frameWorkVersion,\n\t}\n\tms := &discovery.MicroService{\n\t\tServiceName: svcIns.Name,\n\t\tAppId:       appID,\n\t\tVersion:     svcIns.Version,\n\t\tEnvironment: env,\n\t\tFramework:   fw,\n\t}\n\t// attempt to register the microservice\n\tsid, err := r.cli.RegisterService(ms)\n\t// if it fails, it may indicate that the service is already registered\n\tif err != nil {\n\t\tregistryException, ok := err.(*sc.RegistryException)\n\t\tif !ok {\n\t\t\treturn err\n\t\t}\n\t\tvar svcErr errsvc.Error\n\t\tparseErr := json.Unmarshal([]byte(registryException.Message), &svcErr)\n\t\tif parseErr != nil {\n\t\t\treturn parseErr\n\t\t}\n\t\t// if the error code is not specific to the service already existing, return the current error\n\t\tif svcErr.Code != discovery.ErrServiceAlreadyExists {\n\t\t\treturn err\n\t\t}\n\t\tsid, err = r.cli.GetMicroServiceID(appID, ms.ServiceName, ms.Version, ms.Environment)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t} else {\n\t\t// save the service ID for the newly registered service\n\t\tcurServiceID = sid\n\t}\n\tif svcIns.ID == \"\" {\n\t\tvar id uuid.UUID\n\t\tid, err = uuid.NewV4()\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tsvcIns.ID = id.String()\n\t}\n\tprops := map[string]string{\n\t\tappIDKey: appID,\n\t\tenvKey:   env,\n\t}\n\t_, err = r.cli.RegisterMicroServiceInstance(&discovery.MicroServiceInstance{\n\t\tInstanceId: svcIns.ID,\n\t\tServiceId:  sid,\n\t\tEndpoints:  svcIns.Endpoints,\n\t\tHostName:   svcIns.ID,\n\t\tProperties: props,\n\t\tVersion:    svcIns.Version,\n\t})\n\tif err != nil {\n\t\treturn err\n\t}\n\tgo func() {\n\t\tticker := time.NewTicker(30 * time.Second)\n\t\tdefer ticker.Stop()\n\t\tfor {\n\t\t\t<-ticker.C\n\t\t\t_, err = r.cli.Heartbeat(sid, svcIns.ID)\n\t\t\tif err != nil {\n\t\t\t\tlog.Errorf(\"failed to send heartbeat: %v\", err)\n\t\t\t\tcontinue\n\t\t\t}\n\t\t}\n\t}()\n\treturn nil\n}\n\nfunc (r *Registry) Deregister(_ context.Context, svcIns *registry.ServiceInstance) error {\n\tsid, err := r.cli.GetMicroServiceID(appID, svcIns.Name, svcIns.Version, env)\n\tif err != nil {\n\t\treturn err\n\t}\n\t_, err = r.cli.UnregisterMicroServiceInstance(sid, svcIns.ID)\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "contrib/registry/servicecomb/registry_test.go",
    "content": "package servicecomb\n\nimport (\n\t\"context\"\n\t\"testing\"\n\n\tpb \"github.com/go-chassis/cari/discovery\"\n\t\"github.com/go-chassis/sc-client\"\n\t\"github.com/gofrs/uuid\"\n\n\t\"github.com/go-kratos/kratos/v2/registry\"\n)\n\nvar r *Registry\n\nfunc init() {\n\tr = NewRegistry(&mockClient{})\n}\n\ntype mockClient struct{}\n\nfunc (receiver *mockClient) WatchMicroService(_ string, _ func(*sc.MicroServiceInstanceChangedEvent)) error {\n\treturn nil\n}\n\n// nolint\nfunc (receiver *mockClient) FindMicroServiceInstances(_,\n\t_, microServiceName, _ string, _ ...sc.CallOption,\n) ([]*pb.MicroServiceInstance, error) {\n\tif microServiceName == \"KratosServicecomb\" {\n\t\treturn []*pb.MicroServiceInstance{{}}, nil\n\t}\n\treturn nil, nil\n}\n\nfunc (receiver *mockClient) RegisterService(_ *pb.MicroService) (string, error) {\n\treturn \"\", nil\n}\n\nfunc (receiver *mockClient) RegisterMicroServiceInstance(_ *pb.MicroServiceInstance) (string, error) {\n\treturn \"\", nil\n}\n\nfunc (receiver *mockClient) Heartbeat(_, _ string) (bool, error) {\n\treturn true, nil\n}\n\nfunc (receiver *mockClient) UnregisterMicroServiceInstance(_, _ string) (bool, error) {\n\treturn true, nil\n}\n\nfunc (receiver *mockClient) GetMicroServiceID(_, _, _, _ string, _ ...sc.CallOption) (string, error) {\n\treturn \"\", nil\n}\n\nfunc TestRegistry(t *testing.T) {\n\tinstanceID, err := uuid.NewV4()\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tsvc := &registry.ServiceInstance{\n\t\tName:      \"KratosServicecomb\",\n\t\tVersion:   \"0.0.1\",\n\t\tMetadata:  map[string]string{\"app\": \"kratos\"},\n\t\tEndpoints: []string{\"tcp://127.0.0.1:9000?isSecure=false\"},\n\t\tID:        instanceID.String(),\n\t}\n\tctx := context.TODO()\n\tt.Run(\"Register test, expected: success.\", func(t *testing.T) {\n\t\terr = r.Register(ctx, svc)\n\t\tif err != nil {\n\t\t\tt.Error(err)\n\t\t}\n\t})\n\tt.Run(\"GetService test, expected: success.\", func(t *testing.T) {\n\t\tvar insts []*registry.ServiceInstance\n\t\tinsts, err = r.GetService(ctx, svc.Name)\n\t\tif err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\t\tif len(insts) <= 0 {\n\t\t\tt.Errorf(\"inst len less than 0\")\n\t\t}\n\t})\n\tt.Run(\"Deregister test, expected: success.\", func(t *testing.T) {\n\t\tsvc.ID = instanceID.String()\n\t\terr = r.Deregister(ctx, svc)\n\t\tif err != nil {\n\t\t\tt.Error(err)\n\t\t}\n\t})\n}\n\nfunc TestWatcher(t *testing.T) {\n\tinstanceID1, err := uuid.NewV4()\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tsvc1 := &registry.ServiceInstance{\n\t\tName:      \"WatcherTest\",\n\t\tVersion:   \"0.0.1\",\n\t\tMetadata:  map[string]string{\"app\": \"kratos\"},\n\t\tEndpoints: []string{\"tcp://127.0.0.1:9000?isSecure=false\"},\n\t\tID:        instanceID1.String(),\n\t}\n\tctx := context.TODO()\n\terr = r.Register(ctx, svc1)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tw, err := r.Watch(ctx, \"WatcherTest\")\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif w == nil {\n\t\tt.Fatal(\"w is nil\")\n\t}\n\tsbWatcher := w.(*Watcher)\n\tt.Run(\"Watch register event, expected: success\", func(t *testing.T) {\n\t\tgo sbWatcher.Put(svc1)\n\t\tvar instances []*registry.ServiceInstance\n\t\tinstances, err = w.Next()\n\t\tif err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\t\tif len(instances) == 0 {\n\t\t\tt.Errorf(\"instances is empty\")\n\t\t}\n\t\tif instanceID1.String() != instances[0].ID {\n\t\t\tt.Errorf(\"expected %v, got %v\", instanceID1.String(), instances[0].ID)\n\t\t}\n\t})\n\tt.Run(\"Watch deregister event, expected: success\", func(t *testing.T) {\n\t\t// Deregister instance1.\n\t\terr = r.Deregister(ctx, svc1)\n\t\tif err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\t\tgo sbWatcher.Put(svc1)\n\t\tvar instances []*registry.ServiceInstance\n\t\tinstances, err = w.Next()\n\t\tif err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\t\tif len(instances) == 0 {\n\t\t\tt.Errorf(\"instances is empty\")\n\t\t}\n\t\tif instanceID1.String() != instances[0].ID {\n\t\t\tt.Errorf(\"expected %v, got %v\", instanceID1.String(), instances[0].ID)\n\t\t}\n\t})\n\tt.Run(\"Stop test, expected: success\", func(t *testing.T) {\n\t\terr = w.Stop()\n\t\tif err != nil {\n\t\t\tt.Error(err)\n\t\t}\n\t})\n}\n"
  },
  {
    "path": "contrib/registry/servicecomb/watcher.go",
    "content": "package servicecomb\n\nimport (\n\t\"context\"\n\n\t\"github.com/go-chassis/sc-client\"\n\n\t\"github.com/go-kratos/kratos/v2/registry\"\n)\n\nvar _ registry.Watcher = (*Watcher)(nil)\n\ntype Watcher struct {\n\tcli RegistryClient\n\tch  chan *registry.ServiceInstance\n}\n\nfunc newWatcher(_ context.Context, cli RegistryClient, serviceName string) (*Watcher, error) {\n\t// establish dependency relationship between the current service and the target service for discovery\n\t_, err := cli.FindMicroServiceInstances(curServiceID, appID, serviceName, \"\")\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tw := &Watcher{\n\t\tcli: cli,\n\t\tch:  make(chan *registry.ServiceInstance),\n\t}\n\tgo func() {\n\t\twatchErr := w.cli.WatchMicroService(curServiceID, func(event *sc.MicroServiceInstanceChangedEvent) {\n\t\t\tif event.Key.ServiceName != serviceName {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tsvcIns := &registry.ServiceInstance{\n\t\t\t\tID:        event.Instance.InstanceId,\n\t\t\t\tName:      event.Key.ServiceName,\n\t\t\t\tVersion:   event.Key.Version,\n\t\t\t\tMetadata:  event.Instance.Properties,\n\t\t\t\tEndpoints: event.Instance.Endpoints,\n\t\t\t}\n\t\t\tw.Put(svcIns)\n\t\t})\n\t\tif watchErr != nil {\n\t\t\treturn\n\t\t}\n\t}()\n\treturn w, nil\n}\n\n// Put only for UT\nfunc (w *Watcher) Put(svcIns *registry.ServiceInstance) {\n\tw.ch <- svcIns\n}\n\nfunc (w *Watcher) Next() ([]*registry.ServiceInstance, error) {\n\tvar svcInstances []*registry.ServiceInstance\n\tsvcIns := <-w.ch\n\tsvcInstances = append(svcInstances, svcIns)\n\treturn svcInstances, nil\n}\n\nfunc (w *Watcher) Stop() error {\n\tclose(w.ch)\n\treturn nil\n}\n"
  },
  {
    "path": "contrib/registry/zookeeper/go.mod",
    "content": "module github.com/go-kratos/kratos/contrib/registry/zookeeper/v2\n\ngo 1.22\n\nrequire (\n\tgithub.com/go-kratos/kratos/v2 v2.9.2\n\tgithub.com/go-zookeeper/zk v1.0.3\n\tgolang.org/x/sync v0.10.0\n)\n\nreplace github.com/go-kratos/kratos/v2 => ../../../\n"
  },
  {
    "path": "contrib/registry/zookeeper/go.sum",
    "content": "github.com/go-zookeeper/zk v1.0.3 h1:7M2kwOsc//9VeeFiPtf+uSJlVpU66x9Ba5+8XK7/TDg=\ngithub.com/go-zookeeper/zk v1.0.3/go.mod h1:nOB03cncLtlp4t+UAkGSV+9beXP/akpekBwL+UX1Qcw=\ngolang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=\ngolang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=\n"
  },
  {
    "path": "contrib/registry/zookeeper/register.go",
    "content": "package zookeeper\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"path\"\n\t\"time\"\n\n\t\"github.com/go-zookeeper/zk\"\n\t\"golang.org/x/sync/singleflight\"\n\n\t\"github.com/go-kratos/kratos/v2/registry\"\n)\n\nvar (\n\t_ registry.Registrar = (*Registry)(nil)\n\t_ registry.Discovery = (*Registry)(nil)\n)\n\n// Option is etcd registry option.\ntype Option func(o *options)\n\ntype options struct {\n\tnamespace string\n\tuser      string\n\tpassword  string\n}\n\n// WithRootPath with registry root path.\nfunc WithRootPath(path string) Option {\n\treturn func(o *options) { o.namespace = path }\n}\n\n// WithDigestACL with registry password.\nfunc WithDigestACL(user string, password string) Option {\n\treturn func(o *options) {\n\t\to.user = user\n\t\to.password = password\n\t}\n}\n\n// Registry is consul registry\ntype Registry struct {\n\topts *options\n\tconn *zk.Conn\n\n\tgroup singleflight.Group\n}\n\nfunc New(conn *zk.Conn, opts ...Option) *Registry {\n\toptions := &options{\n\t\tnamespace: \"/microservices\",\n\t}\n\tfor _, o := range opts {\n\t\to(options)\n\t}\n\treturn &Registry{\n\t\topts: options,\n\t\tconn: conn,\n\t}\n}\n\nfunc (r *Registry) Register(_ context.Context, service *registry.ServiceInstance) error {\n\tvar (\n\t\tdata []byte\n\t\terr  error\n\t)\n\tif err = r.ensureName(r.opts.namespace, []byte(\"\"), 0); err != nil {\n\t\treturn err\n\t}\n\tserviceNamePath := path.Join(r.opts.namespace, service.Name)\n\tif err = r.ensureName(serviceNamePath, []byte(\"\"), 0); err != nil {\n\t\treturn err\n\t}\n\tif data, err = marshal(service); err != nil {\n\t\treturn err\n\t}\n\tservicePath := path.Join(serviceNamePath, service.ID)\n\tif err = r.ensureName(servicePath, data, zk.FlagEphemeral); err != nil {\n\t\treturn err\n\t}\n\tgo r.reRegister(servicePath, data)\n\treturn nil\n}\n\n// Deregister registry service to zookeeper.\nfunc (r *Registry) Deregister(ctx context.Context, service *registry.ServiceInstance) error {\n\tch := make(chan error, 1)\n\tservicePath := path.Join(r.opts.namespace, service.Name, service.ID)\n\tgo func() {\n\t\terr := r.conn.Delete(servicePath, -1)\n\t\tch <- err\n\t}()\n\tvar err error\n\tselect {\n\tcase <-ctx.Done():\n\t\terr = ctx.Err()\n\tcase err = <-ch:\n\t}\n\treturn err\n}\n\n// GetService get services from zookeeper\nfunc (r *Registry) GetService(_ context.Context, serviceName string) ([]*registry.ServiceInstance, error) {\n\tinstances, err, _ := r.group.Do(serviceName, func() (any, error) {\n\t\tserviceNamePath := path.Join(r.opts.namespace, serviceName)\n\t\tservicesID, _, err := r.conn.Children(serviceNamePath)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\titems := make([]*registry.ServiceInstance, 0, len(servicesID))\n\t\tfor _, service := range servicesID {\n\t\t\tservicePath := path.Join(serviceNamePath, service)\n\t\t\tserviceInstanceByte, _, err := r.conn.Get(servicePath)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\titem, err := unmarshal(serviceInstanceByte)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\titems = append(items, item)\n\t\t}\n\t\treturn items, nil\n\t})\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn instances.([]*registry.ServiceInstance), nil\n}\n\nfunc (r *Registry) Watch(ctx context.Context, serviceName string) (registry.Watcher, error) {\n\tprefix := path.Join(r.opts.namespace, serviceName)\n\treturn newWatcher(ctx, prefix, serviceName, r.conn)\n}\n\n// ensureName ensure node exists, if not exist, create and set data\nfunc (r *Registry) ensureName(path string, data []byte, flags int32) error {\n\texists, stat, err := r.conn.Exists(path)\n\tif err != nil {\n\t\treturn err\n\t}\n\t// ephemeral nodes handling after restart\n\t// fixes a race condition if the server crashes without using CreateProtectedEphemeralSequential()\n\tif flags&zk.FlagEphemeral == zk.FlagEphemeral {\n\t\terr = r.conn.Delete(path, stat.Version)\n\t\tif err != nil && !errors.Is(err, zk.ErrNoNode) {\n\t\t\treturn err\n\t\t}\n\t\texists = false\n\t}\n\tif !exists {\n\t\tif len(r.opts.user) > 0 && len(r.opts.password) > 0 {\n\t\t\t_, err = r.conn.Create(path, data, flags, zk.DigestACL(zk.PermAll, r.opts.user, r.opts.password))\n\t\t} else {\n\t\t\t_, err = r.conn.Create(path, data, flags, zk.WorldACL(zk.PermAll))\n\t\t}\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n\n// reRegister re-register data node info when bad connection recovered\nfunc (r *Registry) reRegister(path string, data []byte) {\n\tsessionID := r.conn.SessionID()\n\tticker := time.NewTicker(time.Second)\n\tdefer ticker.Stop()\n\tfor range ticker.C {\n\t\tcur := r.conn.SessionID()\n\t\t// sessionID changed\n\t\tif cur > 0 && sessionID != cur {\n\t\t\t// re-ensureName\n\t\t\tif err := r.ensureName(path, data, zk.FlagEphemeral); err != nil {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tsessionID = cur\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "contrib/registry/zookeeper/register_test.go",
    "content": "package zookeeper\n\nimport (\n\t\"context\"\n\t\"reflect\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/go-zookeeper/zk\"\n\n\t\"github.com/go-kratos/kratos/v2/registry\"\n)\n\nfunc TestRegistry_GetService(t *testing.T) {\n\tconn, _, err := zk.Connect([]string{\"127.0.0.1:2181\"}, time.Second*15)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t\treturn\n\t}\n\tr := New(conn)\n\n\tsvrHello := &registry.ServiceInstance{\n\t\tID:        \"1\",\n\t\tName:      \"hello\",\n\t\tVersion:   \"v1.0.0\",\n\t\tEndpoints: []string{\"127.0.0.1:8080\"},\n\t}\n\n\ttype fields struct {\n\t\tregistry *Registry\n\t}\n\ttype args struct {\n\t\tctx         context.Context\n\t\tserviceName string\n\t}\n\ttests := []struct {\n\t\tname      string\n\t\tfields    fields\n\t\targs      args\n\t\twant      []*registry.ServiceInstance\n\t\twantErr   bool\n\t\tpreFunc   func(t *testing.T)\n\t\tdeferFunc func(t *testing.T)\n\t}{\n\t\t{\n\t\t\tname: \"normal\",\n\t\t\tpreFunc: func(t *testing.T) {\n\t\t\t\terr = r.Register(context.Background(), svrHello)\n\t\t\t\tif err != nil {\n\t\t\t\t\tt.Error(err)\n\t\t\t\t}\n\t\t\t},\n\t\t\tdeferFunc: func(t *testing.T) {\n\t\t\t\terr = r.Deregister(context.Background(), svrHello)\n\t\t\t\tif err != nil {\n\t\t\t\t\tt.Error(err)\n\t\t\t\t}\n\t\t\t},\n\t\t\tfields: fields{\n\t\t\t\tregistry: r,\n\t\t\t},\n\t\t\targs: args{\n\t\t\t\tctx:         context.Background(),\n\t\t\t\tserviceName: svrHello.Name,\n\t\t\t},\n\t\t\twant:    []*registry.ServiceInstance{svrHello},\n\t\t\twantErr: false,\n\t\t},\n\t\t{\n\t\t\tname: \"can't get any\",\n\t\t\tfields: fields{\n\t\t\t\tregistry: r,\n\t\t\t},\n\t\t\targs: args{\n\t\t\t\tctx:         context.Background(),\n\t\t\t\tserviceName: \"helloxxx\",\n\t\t\t},\n\t\t\twant:    nil,\n\t\t\twantErr: true,\n\t\t},\n\t\t{\n\t\t\tname: \"conn close\",\n\t\t\tpreFunc: func(*testing.T) {\n\t\t\t\tconn.Close()\n\t\t\t},\n\t\t\tfields: fields{\n\t\t\t\tregistry: r,\n\t\t\t},\n\t\t\targs: args{\n\t\t\t\tctx:         context.Background(),\n\t\t\t\tserviceName: \"hello\",\n\t\t\t},\n\t\t\twant:    nil,\n\t\t\twantErr: true,\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tif tt.preFunc != nil {\n\t\t\t\ttt.preFunc(t)\n\t\t\t}\n\t\t\tif tt.deferFunc != nil {\n\t\t\t\tdefer tt.deferFunc(t)\n\t\t\t}\n\t\t\tr := tt.fields.registry\n\t\t\tgot, err := r.GetService(tt.args.ctx, tt.args.serviceName)\n\t\t\tif (err != nil) != tt.wantErr {\n\t\t\t\tt.Errorf(\"GetService() error = %v, wantErr %v\", err, tt.wantErr)\n\t\t\t\tt.Errorf(\"GetService() got = %v\", got)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif !reflect.DeepEqual(got, tt.want) {\n\t\t\t\tt.Errorf(\"GetService() got = %v, want %v\", got, tt.want)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestRegistry_Register(t *testing.T) {\n\tconn, _, err := zk.Connect([]string{\"127.0.0.1:2181\"}, time.Second*15)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t\treturn\n\t}\n\tr := New(conn)\n\n\tsvrHello := &registry.ServiceInstance{\n\t\tID:        \"1\",\n\t\tName:      \"hello\",\n\t\tVersion:   \"v1.0.0\",\n\t\tEndpoints: []string{\"127.0.0.1:8080\"},\n\t}\n\n\ttype fields struct {\n\t\tregistry *Registry\n\t}\n\ttype args struct {\n\t\tctx     context.Context\n\t\tservice *registry.ServiceInstance\n\t}\n\ttests := []struct {\n\t\tname      string\n\t\tfields    fields\n\t\targs      args\n\t\twantErr   bool\n\t\tpreFunc   func(t *testing.T)\n\t\tdeferFunc func(t *testing.T)\n\t}{\n\t\t{\n\t\t\tname: \"normal\",\n\t\t\tfields: fields{\n\t\t\t\tregistry: r,\n\t\t\t},\n\t\t\targs: args{\n\t\t\t\tctx:     context.Background(),\n\t\t\t\tservice: svrHello,\n\t\t\t},\n\t\t\twantErr: false,\n\t\t},\n\t\t{\n\t\t\tname: \"invalid path\",\n\t\t\tfields: fields{\n\t\t\t\tregistry: New(conn, WithRootPath(\"invalid\")),\n\t\t\t},\n\t\t\targs: args{\n\t\t\t\tctx: context.Background(),\n\t\t\t\tservice: &registry.ServiceInstance{\n\t\t\t\t\tID:        \"1\",\n\t\t\t\t\tName:      \"hello1\",\n\t\t\t\t\tVersion:   \"v1.0.0\",\n\t\t\t\t\tEndpoints: []string{\"127.0.0.1:8080\"},\n\t\t\t\t},\n\t\t\t},\n\t\t\twantErr: true,\n\t\t},\n\t\t{\n\t\t\tname: \"auth\",\n\t\t\tpreFunc: func(t *testing.T) {\n\t\t\t\terr = conn.AddAuth(\"digest\", []byte(\"test:test\"))\n\t\t\t\tif err != nil {\n\t\t\t\t\tt.Error(err)\n\t\t\t\t}\n\t\t\t},\n\t\t\tfields: fields{\n\t\t\t\tregistry: New(conn, WithRootPath(\"/tt1\"), WithDigestACL(\"test\", \"test\")),\n\t\t\t},\n\t\t\targs: args{\n\t\t\t\tctx: context.Background(),\n\t\t\t\tservice: &registry.ServiceInstance{\n\t\t\t\t\tID:        \"1\",\n\t\t\t\t\tName:      \"hello2\",\n\t\t\t\t\tVersion:   \"v1.0.0\",\n\t\t\t\t\tEndpoints: []string{\"127.0.0.1:8080\"},\n\t\t\t\t},\n\t\t\t},\n\t\t\twantErr: false,\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tif tt.preFunc != nil {\n\t\t\t\ttt.preFunc(t)\n\t\t\t}\n\t\t\tif tt.deferFunc != nil {\n\t\t\t\tdefer tt.deferFunc(t)\n\t\t\t}\n\t\t\tr := tt.fields.registry\n\t\t\tif err := r.Register(tt.args.ctx, tt.args.service); (err != nil) != tt.wantErr {\n\t\t\t\tt.Errorf(\"Register() error = %v, wantErr %v\", err, tt.wantErr)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestRegistry_Deregister(t *testing.T) {\n\tconn, _, err := zk.Connect([]string{\"127.0.0.1:2181\"}, time.Second*15)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t\treturn\n\t}\n\tr := New(conn)\n\n\tsvrHello := &registry.ServiceInstance{\n\t\tID:        \"1\",\n\t\tName:      \"hello\",\n\t\tVersion:   \"v1.0.0\",\n\t\tEndpoints: []string{\"127.0.0.1:8080\"},\n\t}\n\n\tcancelCtx, cancel := context.WithCancel(context.Background())\n\tcancel()\n\ttype fields struct {\n\t\tregistry *Registry\n\t}\n\ttype args struct {\n\t\tctx     context.Context\n\t\tservice *registry.ServiceInstance\n\t}\n\ttests := []struct {\n\t\tname      string\n\t\tfields    fields\n\t\targs      args\n\t\twantErr   bool\n\t\tpreFunc   func(t *testing.T)\n\t\tdeferFunc func(t *testing.T)\n\t}{\n\t\t{\n\t\t\tname: \"normal\",\n\t\t\tfields: fields{\n\t\t\t\tregistry: r,\n\t\t\t},\n\t\t\targs: args{\n\t\t\t\tctx:     context.Background(),\n\t\t\t\tservice: svrHello,\n\t\t\t},\n\t\t\twantErr: false,\n\t\t\tpreFunc: func(t *testing.T) {\n\t\t\t\terr = r.Register(context.Background(), svrHello)\n\t\t\t\tif err != nil {\n\t\t\t\t\tt.Error(err)\n\t\t\t\t}\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"with ctx cancel\",\n\t\t\tfields: fields{\n\t\t\t\tregistry: r,\n\t\t\t},\n\t\t\targs: args{\n\t\t\t\tctx:     cancelCtx,\n\t\t\t\tservice: svrHello,\n\t\t\t},\n\t\t\twantErr: true,\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tr := tt.fields.registry\n\t\t\tif err := r.Deregister(tt.args.ctx, tt.args.service); (err != nil) != tt.wantErr {\n\t\t\t\tt.Errorf(\"Deregister() error = %v, wantErr %v\", err, tt.wantErr)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestRegistry_Watch(t *testing.T) {\n\tconn, _, err := zk.Connect([]string{\"127.0.0.1:2181\"}, time.Second*15)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t\treturn\n\t}\n\tcloseConn, _, err := zk.Connect([]string{\"127.0.0.1:2181\"}, time.Second*15)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t\treturn\n\t}\n\tr := New(conn)\n\n\tsvrHello := &registry.ServiceInstance{\n\t\tID:        \"1\",\n\t\tName:      \"hello\",\n\t\tVersion:   \"v1.0.0\",\n\t\tEndpoints: []string{\"127.0.0.1:8080\"},\n\t}\n\n\tcancelCtx, cancel := context.WithCancel(context.Background())\n\ttype fields struct {\n\t\tregistry *Registry\n\t}\n\ttype args struct {\n\t\tctx         context.Context\n\t\tserviceName string\n\t}\n\ttests := []struct {\n\t\tname    string\n\t\tfields  fields\n\t\targs    args\n\t\twantErr bool\n\t\twant    []*registry.ServiceInstance\n\n\t\tpreFunc     func(t *testing.T)\n\t\tdeferFunc   func(t *testing.T)\n\t\tprocessFunc func(t *testing.T, w registry.Watcher)\n\t}{\n\t\t{\n\t\t\tname: \"normal\",\n\t\t\tfields: fields{\n\t\t\t\tregistry: r,\n\t\t\t},\n\t\t\targs: args{\n\t\t\t\tctx:         context.Background(),\n\t\t\t\tserviceName: svrHello.Name,\n\t\t\t},\n\t\t\twantErr: false,\n\t\t\twant:    []*registry.ServiceInstance{svrHello},\n\t\t\tdeferFunc: func(t *testing.T) {\n\t\t\t\terr = r.Deregister(context.Background(), svrHello)\n\t\t\t\tif err != nil {\n\t\t\t\t\tt.Error(err)\n\t\t\t\t}\n\t\t\t},\n\t\t\tprocessFunc: func(t *testing.T, _ registry.Watcher) {\n\t\t\t\terr = r.Register(context.Background(), svrHello)\n\t\t\t\tif err != nil {\n\t\t\t\t\tt.Error(err)\n\t\t\t\t}\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"ctx cancel\",\n\t\t\tfields: fields{\n\t\t\t\tregistry: r,\n\t\t\t},\n\t\t\targs: args{\n\t\t\t\tctx:         cancelCtx,\n\t\t\t\tserviceName: svrHello.Name,\n\t\t\t},\n\t\t\twantErr: true,\n\t\t\twant:    nil,\n\t\t\tprocessFunc: func(*testing.T, registry.Watcher) {\n\t\t\t\tcancel()\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"disconnect\",\n\t\t\tfields: fields{\n\t\t\t\tregistry: New(closeConn),\n\t\t\t},\n\t\t\targs: args{\n\t\t\t\tctx:         context.Background(),\n\t\t\t\tserviceName: svrHello.Name,\n\t\t\t},\n\t\t\twantErr: true,\n\t\t\twant:    nil,\n\t\t\tprocessFunc: func(*testing.T, registry.Watcher) {\n\t\t\t\tcloseConn.Close()\n\t\t\t},\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tif tt.preFunc != nil {\n\t\t\t\ttt.preFunc(t)\n\t\t\t}\n\t\t\tif tt.deferFunc != nil {\n\t\t\t\tdefer tt.deferFunc(t)\n\t\t\t}\n\t\t\tr := tt.fields.registry\n\t\t\twatcher, err := r.Watch(tt.args.ctx, tt.args.serviceName)\n\t\t\tif err != nil {\n\t\t\t\tt.Error(err)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tdefer func() {\n\t\t\t\terr = watcher.Stop()\n\t\t\t\tif err != nil {\n\t\t\t\t\tt.Error(err)\n\t\t\t\t}\n\t\t\t}()\n\t\t\t_, err = watcher.Next()\n\t\t\tif err != nil {\n\t\t\t\tt.Error(err)\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tif tt.processFunc != nil {\n\t\t\t\ttt.processFunc(t, watcher)\n\t\t\t}\n\n\t\t\twant, err := watcher.Next()\n\t\t\tif (err != nil) != tt.wantErr {\n\t\t\t\tt.Errorf(\"Watch() error = %v, wantErr %v\", err, tt.wantErr)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif !reflect.DeepEqual(want, tt.want) {\n\t\t\t\tt.Errorf(\"Watch() watcher = %v, want %v\", watcher, tt.want)\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "contrib/registry/zookeeper/service.go",
    "content": "package zookeeper\n\nimport (\n\t\"encoding/json\"\n\n\t\"github.com/go-kratos/kratos/v2/registry\"\n)\n\nfunc marshal(si *registry.ServiceInstance) ([]byte, error) {\n\treturn json.Marshal(si)\n}\n\nfunc unmarshal(data []byte) (si *registry.ServiceInstance, err error) {\n\terr = json.Unmarshal(data, &si)\n\treturn\n}\n"
  },
  {
    "path": "contrib/registry/zookeeper/watcher.go",
    "content": "package zookeeper\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"path\"\n\t\"sync/atomic\"\n\n\t\"github.com/go-zookeeper/zk\"\n\n\t\"github.com/go-kratos/kratos/v2/registry\"\n)\n\nvar _ registry.Watcher = (*watcher)(nil)\n\nvar ErrWatcherStopped = errors.New(\"watcher stopped\")\n\ntype watcher struct {\n\tctx    context.Context\n\tevent  chan zk.Event\n\tconn   *zk.Conn\n\tcancel context.CancelFunc\n\n\tfirst atomic.Bool\n\t// prefix for ZooKeeper paths or keys (used for filtering or identifying watched nodes)\n\tprefix string\n\t// the name of the service being watched in ZooKeeper\n\tserviceName string\n}\n\nfunc newWatcher(ctx context.Context, prefix, serviceName string, conn *zk.Conn) (*watcher, error) {\n\tw := &watcher{conn: conn, event: make(chan zk.Event, 1), prefix: prefix, serviceName: serviceName}\n\tw.ctx, w.cancel = context.WithCancel(ctx)\n\tgo w.watch(w.ctx)\n\treturn w, nil\n}\n\nfunc (w *watcher) watch(ctx context.Context) {\n\tfor {\n\t\t// since a single watch is only valid for one event, we need to loop to continue watching\n\t\t_, _, ch, err := w.conn.ChildrenW(w.prefix)\n\t\tif err != nil {\n\t\t\t// If the target service node has not been created\n\t\t\tif errors.Is(err, zk.ErrNoNode) {\n\t\t\t\t// Add watcher for the node exists\n\t\t\t\t_, _, ch, err = w.conn.ExistsW(w.prefix)\n\t\t\t}\n\t\t\tif err != nil {\n\t\t\t\tw.event <- zk.Event{Err: err}\n\t\t\t\tcontinue\n\t\t\t}\n\t\t}\n\t\tselect {\n\t\tcase <-ctx.Done():\n\t\t\treturn\n\t\tcase ev := <-ch:\n\t\t\tw.event <- ev\n\t\t}\n\t}\n}\n\nfunc (w *watcher) Next() ([]*registry.ServiceInstance, error) {\n\t// TODO: multiple calls to Next may lead to inconsistent service instance information\n\tif w.first.CompareAndSwap(false, true) {\n\t\treturn w.getServices()\n\t}\n\tselect {\n\tcase <-w.ctx.Done():\n\t\treturn nil, w.ctx.Err()\n\tcase e := <-w.event:\n\t\tif e.State == zk.StateDisconnected {\n\t\t\treturn nil, ErrWatcherStopped\n\t\t}\n\t\tif e.Err != nil {\n\t\t\treturn nil, e.Err\n\t\t}\n\t\treturn w.getServices()\n\t}\n}\n\nfunc (w *watcher) Stop() error {\n\tw.cancel()\n\treturn nil\n}\n\nfunc (w *watcher) getServices() ([]*registry.ServiceInstance, error) {\n\tservicesID, _, err := w.conn.Children(w.prefix)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\titems := make([]*registry.ServiceInstance, 0, len(servicesID))\n\tfor _, id := range servicesID {\n\t\tservicePath := path.Join(w.prefix, id)\n\t\tb, _, err := w.conn.Get(servicePath)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\titem, err := unmarshal(b)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\t// if the service name of the retrieved instance does not match the watcher's service name, skip it\n\t\tif item.Name != w.serviceName {\n\t\t\tcontinue\n\t\t}\n\n\t\titems = append(items, item)\n\t}\n\treturn items, nil\n}\n"
  },
  {
    "path": "contrib/transport/mcp/README.md",
    "content": "# MCP Transport\n\nThis module implements the MCP server in Kratos based on mcp-go.\n\n[![go.dev reference](https://img.shields.io/badge/go.dev-reference-007d9c?logo=go&logoColor=white&style=flat-square)](https://pkg.go.dev/github.com/go-kratos/kratos/contrib/transport/mcp/v2)\n\n## Quick start\n```go\nimport(\n    tm \"github.com/go-kratos/kratos/contrib/transport/mcp/v2\"\n    mcp \"github.com/mark3labs/mcp-go/mcp\"\n)\n\nfunc helloHandler(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {\n    name, ok := request.Params.Arguments[\"name\"].(string)\n    if !ok {\n        return nil, errors.New(\"name must be a string\")\n    }\n    return mcp.NewToolResultText(fmt.Sprintf(\"Hello, %s!\", name)), nil\n}\n\nfunc Health(next http.Handler) http.Handler {\n\treturn http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\tif r.URL.Path == \"/health/ready\" {\n\t\t\tw.WriteHeader(http.StatusOK)\n\t\t\treturn\n\t\t}\n\t\tnext.ServeHTTP(w, r)\n\t})\n}\n\nfunc main() {\n    srv := tm.NewServer(\"kratos-mcp\", \"v1.0.0\", tm.Address(\":8000\"), tm.Middleware(Health))\n    tool := mcp.NewTool(\"hello_world\",\n        mcp.WithDescription(\"Say hello to someone\"),\n        mcp.WithString(\"name\",\n            mcp.Required(),\n            mcp.Description(\"Name of the person to greet\"),\n        ),\n    )\n    // Add tool handler\n    srv.AddTool(tool, helloHandler)\n    // creates a kratos application\n    app := kratos.New(\n        kratos.Name(\"kratos-app\"),\n        kratos.Server(srv),\n    )\n    if err := app.Run(); err != nil {\n        panic(err)\n    }\n}\n```\n"
  },
  {
    "path": "contrib/transport/mcp/go.mod",
    "content": "module github.com/go-kratos/kratos/contrib/transport/mcp/v2\n\ngo 1.23\n\ntoolchain go1.24.6\n\nrequire (\n\tgithub.com/go-kratos/kratos/v2 v2.9.2\n\tgithub.com/mark3labs/mcp-go v0.23.0\n)\n\nrequire (\n\tgithub.com/go-playground/form/v4 v4.2.0 // indirect\n\tgithub.com/google/uuid v1.6.0 // indirect\n\tgithub.com/spf13/cast v1.7.1 // indirect\n\tgithub.com/yosida95/uritemplate/v3 v3.0.2 // indirect\n\tgoogle.golang.org/protobuf v1.33.0 // indirect\n\tgopkg.in/yaml.v3 v3.0.1 // indirect\n)\n\nreplace github.com/go-kratos/kratos/v2 => ../../../\n"
  },
  {
    "path": "contrib/transport/mcp/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/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A=\ngithub.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=\ngithub.com/go-playground/form/v4 v4.2.0 h1:N1wh+Goz61e6w66vo8vJkQt+uwZSoLz50kZPJWR8eic=\ngithub.com/go-playground/form/v4 v4.2.0/go.mod h1:q1a2BY+AQUUzhl6xA/6hBetay6dEIhMHjgvJiGo6K7U=\ngithub.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=\ngithub.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=\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/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/mark3labs/mcp-go v0.23.0 h1:NmtoPx4jf7if7bfAynocpVdpXqX5U8X/18c1gddK/QA=\ngithub.com/mark3labs/mcp-go v0.23.0/go.mod h1:rXqOudj/djTORU/ThxYx8fqEVj/5pvTuuebQ2RC7uk4=\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.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=\ngithub.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=\ngithub.com/spf13/cast v1.7.1 h1:cuNEagBQEHWN1FnbGEjCXL2szYEXqfJPbP2HNUaca9Y=\ngithub.com/spf13/cast v1.7.1/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=\ngithub.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=\ngithub.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=\ngithub.com/yosida95/uritemplate/v3 v3.0.2 h1:Ed3Oyj9yrmi9087+NczuL5BwkIc4wvTb5zIM+UJPGz4=\ngithub.com/yosida95/uritemplate/v3 v3.0.2/go.mod h1:ILOh0sOhIJR3+L/8afwt/kE++YT040gmv5BQTMR2HP4=\ngoogle.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=\ngoogle.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=\ngopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/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.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=\ngopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\n"
  },
  {
    "path": "contrib/transport/mcp/server.go",
    "content": "package mcp\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"net/url\"\n\n\t\"github.com/go-kratos/kratos/v2/transport\"\n\n\t\"github.com/mark3labs/mcp-go/server\"\n)\n\nvar (\n\t_ transport.Server     = (*Server)(nil)\n\t_ transport.Endpointer = (*Server)(nil)\n\t_ http.Handler         = (*Server)(nil)\n)\n\n// MiddlewareFunc is a function that takes an http.Handler and returns an http.Handler.\ntype MiddlewareFunc func(http.Handler) http.Handler\n\n// ServerOption is an HTTP server option.\ntype ServerOption func(*Server)\n\n// Address with server address.\nfunc Address(addr string) ServerOption {\n\treturn func(s *Server) {\n\t\ts.address = addr\n\t}\n}\n\n// Endpoint with server address.\nfunc Endpoint(endpoint *url.URL) ServerOption {\n\treturn func(s *Server) {\n\t\ts.endpoint = endpoint\n\t}\n}\n\n// Middleware with server middleware.\nfunc Middleware(m MiddlewareFunc) ServerOption {\n\treturn func(s *Server) {\n\t\ts.middleware = m\n\t}\n}\n\n// SrvOptions with server options.\nfunc SrvOptions(opts ...server.ServerOption) ServerOption {\n\treturn func(s *Server) {\n\t\ts.srvOpts = append(s.srvOpts, opts...)\n\t}\n}\n\n// SSEOptions with server SSE options.\nfunc SSEOptions(opts ...server.SSEOption) ServerOption {\n\treturn func(s *Server) {\n\t\ts.sseOpts = append(s.sseOpts, opts...)\n\t}\n}\n\n// Server is a MCP server.\ntype Server struct {\n\t*server.MCPServer\n\tsrv        *http.Server\n\tsse        *server.SSEServer\n\tmiddleware MiddlewareFunc\n\taddress    string\n\tendpoint   *url.URL\n\tsrvOpts    []server.ServerOption\n\tsseOpts    []server.SSEOption\n}\n\n// NewServer creates a new MCP server.\nfunc NewServer(name, version string, opts ...ServerOption) *Server {\n\tsrv := &Server{\n\t\taddress:    \":8000\",\n\t\tmiddleware: func(next http.Handler) http.Handler { return next },\n\t}\n\tfor _, o := range opts {\n\t\to(srv)\n\t}\n\tsrv.MCPServer = server.NewMCPServer(name, version, srv.srvOpts...)\n\tsrv.srv = &http.Server{Addr: srv.address, Handler: srv.middleware(srv)}\n\tsrv.sse = server.NewSSEServer(srv.MCPServer, append(srv.sseOpts, server.WithHTTPServer(srv.srv))...)\n\treturn srv\n}\n\n// ServeHTTP implements the http.Handler interface.\nfunc (s *Server) ServeHTTP(res http.ResponseWriter, req *http.Request) {\n\ts.sse.ServeHTTP(res, req)\n}\n\n// Endpoint return a real address to registry endpoint.\n// examples:\n// - http://127.0.0.1:8000\nfunc (s *Server) Endpoint() (*url.URL, error) {\n\tif s.endpoint != nil {\n\t\treturn s.endpoint, nil\n\t}\n\treturn url.Parse(fmt.Sprintf(\"http://%s\", s.address))\n}\n\n// Start start the MCP server.\nfunc (s *Server) Start(_ context.Context) error {\n\tif err := s.srv.ListenAndServe(); err != nil {\n\t\tif !errors.Is(err, http.ErrServerClosed) {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n\n// Stop stop the MCP server.\nfunc (s *Server) Stop(ctx context.Context) error {\n\treturn s.sse.Shutdown(ctx)\n}\n"
  },
  {
    "path": "contrib/transport/mcp/server_test.go",
    "content": "package mcp\n\nimport (\n\t\"context\"\n\t\"testing\"\n\t\"time\"\n)\n\nfunc TestServer(t *testing.T) {\n\tvar (\n\t\tctx = context.Background()\n\t\tsrv = NewServer(\"test\", \"v1.0.0\", Address(\":0\"))\n\t)\n\tgo func() {\n\t\tif err := srv.Start(ctx); err != nil {\n\t\t\tpanic(err)\n\t\t}\n\t}()\n\ttime.Sleep(time.Second)\n\tif err := srv.Stop(ctx); err != nil {\n\t\tt.Fatal(err)\n\t}\n}\n"
  },
  {
    "path": "docs/README.md",
    "content": ""
  },
  {
    "path": "docs/design/kratos-v2.md",
    "content": "# Kratos v2 Kit Design\n\nMaoJian\n\nLast updated: December 25, 2020\n\n## Abstract\nkratos v1 基础库主要专注在各类功能的细节实现，比如 gRPC 的负载均衡，熔断器等一系列微服务需要的功能。\n\n## Background\n\n## Proposal\n\n## Implementation\n"
  },
  {
    "path": "encoding/README.md",
    "content": "# encoding\n\n## msgpack\n\n```shell\ngo get -u github.com/go-kratos/kratos/contrib/encoding/msgpack/v2\n```\n"
  },
  {
    "path": "encoding/encoding.go",
    "content": "package encoding\n\nimport (\n\t\"strings\"\n)\n\n// Codec defines the interface Transport uses to encode and decode messages. Note\n// that implementations of this interface must be thread safe; a Codec's\n// methods can be called from concurrent goroutines.\ntype Codec interface {\n\t// Marshal returns the wire format of v.\n\tMarshal(v any) ([]byte, error)\n\t// Unmarshal parses the wire format into v.\n\tUnmarshal(data []byte, v any) error\n\t// Name returns the name of the Codec implementation. The returned string\n\t// will be used as part of content type in transmission. The result must be\n\t// static; the result cannot change between calls.\n\tName() string\n}\n\nvar registeredCodecs = make(map[string]Codec)\n\n// RegisterCodec registers the provided Codec for use with all Transport clients and\n// servers.\nfunc RegisterCodec(codec Codec) {\n\tif codec == nil {\n\t\tpanic(\"cannot register a nil Codec\")\n\t}\n\tif codec.Name() == \"\" {\n\t\tpanic(\"cannot register Codec with empty string result for Name()\")\n\t}\n\tcontentSubtype := strings.ToLower(codec.Name())\n\tregisteredCodecs[contentSubtype] = codec\n}\n\n// GetCodec gets a registered Codec by content-subtype, or nil if no Codec is\n// registered for the content-subtype.\n//\n// The content-subtype is expected to be lowercase.\nfunc GetCodec(contentSubtype string) Codec {\n\treturn registeredCodecs[contentSubtype]\n}\n"
  },
  {
    "path": "encoding/encoding_test.go",
    "content": "package encoding\n\nimport (\n\t\"encoding/xml\"\n\t\"runtime/debug\"\n\t\"testing\"\n)\n\ntype codec struct{}\n\nfunc (c codec) Marshal(_ any) ([]byte, error) {\n\tpanic(\"implement me\")\n}\n\nfunc (c codec) Unmarshal(_ []byte, _ any) error {\n\tpanic(\"implement me\")\n}\n\nfunc (c codec) Name() string {\n\treturn \"\"\n}\n\n// codec2 is a Codec implementation with xml.\ntype codec2 struct{}\n\nfunc (codec2) Marshal(v any) ([]byte, error) {\n\treturn xml.Marshal(v)\n}\n\nfunc (codec2) Unmarshal(data []byte, v any) error {\n\treturn xml.Unmarshal(data, v)\n}\n\nfunc (codec2) Name() string {\n\treturn \"xml\"\n}\n\nfunc TestRegisterCodec(t *testing.T) {\n\tf := func() { RegisterCodec(nil) }\n\tfuncDidPanic, panicValue, _ := didPanic(f)\n\tif !funcDidPanic {\n\t\tt.Fatalf(\"func should panic\\n\\tPanic value:\\t%#v\", panicValue)\n\t}\n\tif panicValue != \"cannot register a nil Codec\" {\n\t\tt.Fatalf(\"panic error got %s want cannot register a nil Codec\", panicValue)\n\t}\n\tf = func() {\n\t\tRegisterCodec(codec{})\n\t}\n\tfuncDidPanic, panicValue, _ = didPanic(f)\n\tif !funcDidPanic {\n\t\tt.Fatalf(\"func should panic\\n\\tPanic value:\\t%#v\", panicValue)\n\t}\n\tif panicValue != \"cannot register Codec with empty string result for Name()\" {\n\t\tt.Fatalf(\"panic error got %s want cannot register Codec with empty string result for Name()\", panicValue)\n\t}\n\tcodec := codec2{}\n\tRegisterCodec(codec)\n\tgot := GetCodec(\"xml\")\n\tif got != codec {\n\t\tt.Fatalf(\"RegisterCodec(%v) want %v got %v\", codec, codec, got)\n\t}\n}\n\n// PanicTestFunc defines a func that should be passed to assert.Panics and assert.NotPanics\n// methods, and represents a simple func that takes no arguments, and returns nothing.\ntype PanicTestFunc func()\n\n// didPanic returns true if the function passed to it panics. Otherwise, it returns false.\nfunc didPanic(f PanicTestFunc) (bool, any, string) {\n\tdidPanic := false\n\tvar message any\n\tvar stack string\n\tfunc() {\n\t\tdefer func() {\n\t\t\tif message = recover(); message != nil {\n\t\t\t\tdidPanic = true\n\t\t\t\tstack = string(debug.Stack())\n\t\t\t}\n\t\t}()\n\n\t\t// call the target function\n\t\tf()\n\t}()\n\n\treturn didPanic, message, stack\n}\n"
  },
  {
    "path": "encoding/form/form.go",
    "content": "package form\n\nimport (\n\t\"net/url\"\n\t\"reflect\"\n\n\t\"github.com/go-playground/form/v4\"\n\t\"google.golang.org/protobuf/proto\"\n\n\t\"github.com/go-kratos/kratos/v2/encoding\"\n)\n\nconst (\n\t// Name is form codec name\n\tName = \"x-www-form-urlencoded\"\n\t// Null value string\n\tnullStr = \"null\"\n)\n\nvar (\n\tencoder = form.NewEncoder()\n\tdecoder = form.NewDecoder()\n)\n\n// This variable can be replaced with -ldflags like below:\n// go build \"-ldflags=-X github.com/go-kratos/kratos/v2/encoding/form.tagName=form\"\nvar tagName = \"json\"\n\nfunc init() {\n\tdecoder.SetTagName(tagName)\n\tencoder.SetTagName(tagName)\n\tencoding.RegisterCodec(codec{encoder: encoder, decoder: decoder})\n}\n\ntype codec struct {\n\tencoder *form.Encoder\n\tdecoder *form.Decoder\n}\n\nfunc (c codec) Marshal(v any) ([]byte, error) {\n\tvar vs url.Values\n\tvar err error\n\tif m, ok := v.(proto.Message); ok {\n\t\tvs, err = EncodeValues(m)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t} else {\n\t\tvs, err = c.encoder.Encode(v)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\tfor k, v := range vs {\n\t\tif len(v) == 0 {\n\t\t\tdelete(vs, k)\n\t\t}\n\t}\n\treturn []byte(vs.Encode()), nil\n}\n\nfunc (c codec) Unmarshal(data []byte, v any) error {\n\tvs, err := url.ParseQuery(string(data))\n\tif err != nil {\n\t\treturn err\n\t}\n\n\trv := reflect.ValueOf(v)\n\tfor rv.Kind() == reflect.Ptr {\n\t\tif rv.IsNil() {\n\t\t\trv.Set(reflect.New(rv.Type().Elem()))\n\t\t}\n\t\trv = rv.Elem()\n\t}\n\tif m, ok := v.(proto.Message); ok {\n\t\treturn DecodeValues(m, vs)\n\t}\n\tif m, ok := rv.Interface().(proto.Message); ok {\n\t\treturn DecodeValues(m, vs)\n\t}\n\n\treturn c.decoder.Decode(v, vs)\n}\n\nfunc (codec) Name() string {\n\treturn Name\n}\n"
  },
  {
    "path": "encoding/form/form_test.go",
    "content": "package form\n\nimport (\n\t\"encoding/base64\"\n\t\"reflect\"\n\t\"testing\"\n\t\"time\"\n\n\t\"google.golang.org/protobuf/types/known/durationpb\"\n\t\"google.golang.org/protobuf/types/known/fieldmaskpb\"\n\t\"google.golang.org/protobuf/types/known/timestamppb\"\n\t\"google.golang.org/protobuf/types/known/wrapperspb\"\n\n\t\"github.com/go-kratos/kratos/v2/encoding\"\n\tbdtest \"github.com/go-kratos/kratos/v2/internal/testdata/binding\"\n\t\"github.com/go-kratos/kratos/v2/internal/testdata/complex\"\n\tectest \"github.com/go-kratos/kratos/v2/internal/testdata/encoding\"\n)\n\n// This variable can be replaced with -ldflags like below:\n// go test \"-ldflags=-X github.com/go-kratos/kratos/v2/encoding/form.tagNameTest=form\"\nvar tagNameTest string\n\nfunc init() {\n\tif tagNameTest == \"\" {\n\t\ttagNameTest = tagName\n\t}\n}\n\nfunc TestFormEncoderAndDecoder(t *testing.T) {\n\tt.Cleanup(func() {\n\t\tencoder.SetTagName(tagName)\n\t\tdecoder.SetTagName(tagName)\n\t})\n\n\tencoder.SetTagName(tagNameTest)\n\tdecoder.SetTagName(tagNameTest)\n\n\ttype testFormTagName struct {\n\t\tName string `form:\"name_form\" json:\"name_json\"`\n\t}\n\tv, err := encoder.Encode(&testFormTagName{\n\t\tName: \"test tag name\",\n\t})\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tjsonName := v.Get(\"name_json\")\n\tformName := v.Get(\"name_form\")\n\tswitch tagNameTest {\n\tcase \"json\":\n\t\tif jsonName != \"test tag name\" {\n\t\t\tt.Errorf(\"got: %s\", jsonName)\n\t\t}\n\t\tif formName != \"\" {\n\t\t\tt.Errorf(\"want: empty, got: %s\", formName)\n\t\t}\n\tcase \"form\":\n\t\tif formName != \"test tag name\" {\n\t\t\tt.Errorf(\"got: %s\", formName)\n\t\t}\n\t\tif jsonName != \"\" {\n\t\t\tt.Errorf(\"want: empty, got: %s\", jsonName)\n\t\t}\n\tdefault:\n\t\tt.Fatalf(\"unknown tag name: %s\", tagNameTest)\n\t}\n\n\tvar tn *testFormTagName\n\terr = decoder.Decode(&tn, v)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif tn == nil {\n\t\tt.Fatal(\"nil tag name\")\n\t}\n\tif tn.Name != \"test tag name\" {\n\t\tt.Errorf(\"got %s\", tn.Name)\n\t}\n}\n\ntype LoginRequest struct {\n\tUsername string `json:\"username,omitempty\"`\n\tPassword string `json:\"password,omitempty\"`\n}\n\nfunc TestFormCodecMarshal(t *testing.T) {\n\treq := &LoginRequest{\n\t\tUsername: \"kratos\",\n\t\tPassword: \"kratos_pwd\",\n\t}\n\tcontent, err := encoding.GetCodec(Name).Marshal(req)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif !reflect.DeepEqual([]byte(\"password=kratos_pwd&username=kratos\"), content) {\n\t\tt.Errorf(\"expect %s, got %s\", \"password=kratos_pwd&username=kratos\", content)\n\t}\n\n\treq = &LoginRequest{\n\t\tUsername: \"kratos\",\n\t\tPassword: \"\",\n\t}\n\tcontent, err = encoding.GetCodec(Name).Marshal(req)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif !reflect.DeepEqual([]byte(\"username=kratos\"), content) {\n\t\tt.Errorf(\"expect %s, got %s\", \"username=kratos\", content)\n\t}\n\n\tm := struct {\n\t\tID   int32  `json:\"id\"`\n\t\tName string `json:\"name\"`\n\t}{\n\t\tID:   1,\n\t\tName: \"kratos\",\n\t}\n\tcontent, err = encoding.GetCodec(Name).Marshal(m)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif !reflect.DeepEqual([]byte(\"id=1&name=kratos\"), content) {\n\t\tt.Errorf(\"expect %s, got %s\", \"id=1&name=kratos\", content)\n\t}\n}\n\nfunc TestFormCodecUnmarshal(t *testing.T) {\n\treq := &LoginRequest{\n\t\tUsername: \"kratos\",\n\t\tPassword: \"kratos_pwd\",\n\t}\n\tcontent, err := encoding.GetCodec(Name).Marshal(req)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tbindReq := new(LoginRequest)\n\terr = encoding.GetCodec(Name).Unmarshal(content, bindReq)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif !reflect.DeepEqual(\"kratos\", bindReq.Username) {\n\t\tt.Errorf(\"expect %v, got %v\", \"kratos\", bindReq.Username)\n\t}\n\tif !reflect.DeepEqual(\"kratos_pwd\", bindReq.Password) {\n\t\tt.Errorf(\"expect %v, got %v\", \"kratos_pwd\", bindReq.Password)\n\t}\n}\n\n//nolint:staticcheck\nfunc TestProtoEncodeDecode(t *testing.T) {\n\tin := &complex.Complex{\n\t\tId:      2233,\n\t\tNoOne:   \"2233\",\n\t\tSimple:  &complex.Simple{Component: \"5566\"},\n\t\tStrings: []string{\"3344\", \"5566\"},\n\t\tB:       true,\n\t\tSex:     complex.Sex_woman,\n\t\tAge:     18,\n\t\tA:       19,\n\t\tCount:   3,\n\t\tPrice:   11.23,\n\t\tD:       22.22,\n\t\tByte:    []byte(\"123\"),\n\t\tMap:     map[string]string{\"kratos\": \"https://go-kratos.dev/\", \"kratos_start\": \"https://go-kratos.dev/en/docs/getting-started/start/\"},\n\n\t\tTimestamp: timestamppb.New(time.Date(1970, 1, 1, 0, 0, 20, 2, time.Local)),\n\t\tDuration:  &durationpb.Duration{Seconds: 120, Nanos: 22},\n\t\tField:     &fieldmaskpb.FieldMask{Paths: []string{\"1\", \"2\"}},\n\t\tDouble:    &wrapperspb.DoubleValue{Value: 12.33},\n\t\tFloat:     &wrapperspb.FloatValue{Value: 12.34},\n\t\tInt64:     &wrapperspb.Int64Value{Value: 64},\n\t\tInt32:     &wrapperspb.Int32Value{Value: 32},\n\t\tUint64:    &wrapperspb.UInt64Value{Value: 64},\n\t\tUint32:    &wrapperspb.UInt32Value{Value: 32},\n\t\tBool:      &wrapperspb.BoolValue{Value: false},\n\t\tString_:   &wrapperspb.StringValue{Value: \"go-kratos\"},\n\t\tBytes:     &wrapperspb.BytesValue{Value: []byte(\"123\")},\n\t}\n\tcontent, err := encoding.GetCodec(Name).Marshal(in)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif \"a=19&age=18&b=true&bool=false&byte=MTIz&bytes=MTIz&count=3&d=22.22&double=12.33&duration=\"+\n\t\t\"2m0.000000022s&field=1%2C2&float=12.34&id=2233&int32=32&int64=64&\"+\n\t\t\"map%5Bkratos%5D=https%3A%2F%2Fgo-kratos.dev%2F&map%5Bkratos_start%5D=https%3A%2F%2Fgo-kratos.dev%2Fen%2Fdocs%2Fgetting-started%2Fstart%2F&\"+\n\t\t\"numberOne=2233&price=11.23&sex=woman&string=go-kratos&strings=3344&strings=5566\"+\n\t\t\"&timestamp=1970-01-01T00%3A00%3A20.000000002Z&uint32=32&uint64=64&very_simple.component=5566\" != string(content) {\n\t\tt.Errorf(\"rawpath is not equal to %s\", content)\n\t}\n\tin2 := &complex.Complex{}\n\terr = encoding.GetCodec(Name).Unmarshal(content, in2)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif int64(2233) != in2.Id {\n\t\tt.Errorf(\"expect %v, got %v\", int64(2233), in2.Id)\n\t}\n\tif \"2233\" != in2.NoOne {\n\t\tt.Errorf(\"expect %v, got %v\", \"2233\", in2.NoOne)\n\t}\n\tif in2.Simple == nil {\n\t\tt.Errorf(\"expect %v, got %v\", nil, in2.Simple)\n\t}\n\tif \"5566\" != in2.Simple.Component {\n\t\tt.Errorf(\"expect %v, got %v\", \"5566\", in2.Simple.Component)\n\t}\n\tif in2.Strings == nil {\n\t\tt.Errorf(\"expect %v, got %v\", nil, in2.Strings)\n\t}\n\tif len(in2.Strings) != 2 {\n\t\tt.Errorf(\"expect %v, got %v\", 2, len(in2.Strings))\n\t}\n\tif \"3344\" != in2.Strings[0] {\n\t\tt.Errorf(\"expect %v, got %v\", \"3344\", in2.Strings[0])\n\t}\n\tif \"5566\" != in2.Strings[1] {\n\t\tt.Errorf(\"expect %v, got %v\", \"5566\", in2.Strings[1])\n\t}\n\tif l := len(in2.GetMap()); l != 2 {\n\t\tt.Fatalf(\"in2.Map length want: %d, got: %d\", 2, l)\n\t}\n\tfor key, val := range in.GetMap() {\n\t\tif in2Val := in2.GetMap()[key]; in2Val != val {\n\t\t\tt.Errorf(\"%s want: %q, got: %q\", \"map[\"+key+\"]\", val, in2Val)\n\t\t}\n\t}\n}\n\n//nolint:staticcheck\nfunc TestDecodeStructPb(t *testing.T) {\n\treq := new(ectest.StructPb)\n\tquery := `data={\"name\":\"kratos\"}&data_list={\"name1\": \"kratos\"}&data_list={\"name2\": \"go-kratos\"}`\n\tif err := encoding.GetCodec(Name).Unmarshal([]byte(query), req); err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif \"kratos\" != req.Data.GetFields()[\"name\"].GetStringValue() {\n\t\tt.Errorf(\"except %v, got %v\", \"kratos\", req.Data.GetFields()[\"name\"].GetStringValue())\n\t}\n\tif len(req.DataList) != 2 {\n\t\tt.Fatalf(\"except %v, got %v\", 2, len(req.DataList))\n\t}\n\tif \"kratos\" != req.DataList[0].GetFields()[\"name1\"].GetStringValue() {\n\t\tt.Errorf(\"except %v, got %v\", \"kratos\", req.Data.GetFields()[\"name1\"].GetStringValue())\n\t}\n\tif \"go-kratos\" != req.DataList[1].GetFields()[\"name2\"].GetStringValue() {\n\t\tt.Errorf(\"except %v, got %v\", \"go-kratos\", req.Data.GetFields()[\"name2\"].GetStringValue())\n\t}\n}\n\nfunc TestDecodeBytesValuePb(t *testing.T) {\n\turl := \"https://example.com/xx/?a=1&b=2&c=3\"\n\tval := base64.URLEncoding.EncodeToString([]byte(url))\n\tcontent := \"bytes=\" + val\n\tin2 := &complex.Complex{}\n\tif err := encoding.GetCodec(Name).Unmarshal([]byte(content), in2); err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif url != string(in2.Bytes.Value) {\n\t\tt.Errorf(\"except %s, got %s\", val, in2.Bytes.Value)\n\t}\n}\n\nfunc TestEncodeFieldMask(t *testing.T) {\n\treq := &bdtest.HelloRequest{\n\t\tUpdateMask: &fieldmaskpb.FieldMask{Paths: []string{\"foo\", \"bar\"}},\n\t}\n\tif v := EncodeFieldMask(req.ProtoReflect()); v != \"updateMask=foo,bar\" {\n\t\tt.Errorf(\"got %s\", v)\n\t}\n}\n\nfunc TestOptional(t *testing.T) {\n\tv := int32(100)\n\treq := &bdtest.HelloRequest{\n\t\tName:     \"foo\",\n\t\tSub:      &bdtest.Sub{Name: \"bar\"},\n\t\tOptInt32: &v,\n\t}\n\tquery, _ := EncodeValues(req)\n\tif query.Encode() != \"name=foo&optInt32=100&sub.naming=bar\" {\n\t\tt.Fatalf(\"got %s\", query.Encode())\n\t}\n}\n\nfunc TestWithUnsupportedType(t *testing.T) {\n\tin := &complex.Complex{\n\t\tId:      2233,\n\t\tSimples: []*complex.Simple{{Component: \"3344\"}, {Component: \"5566\"}},\n\t}\n\tquery, _ := EncodeValues(in)\n\tif query.Encode() != \"id=2233\" {\n\t\tt.Fatalf(\"got %s\", query.Encode())\n\t}\n}\n"
  },
  {
    "path": "encoding/form/proto_decode.go",
    "content": "package form\n\nimport (\n\t\"encoding/base64\"\n\t\"errors\"\n\t\"fmt\"\n\t\"net/url\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n\n\t\"google.golang.org/protobuf/encoding/protojson\"\n\t\"google.golang.org/protobuf/proto\"\n\t\"google.golang.org/protobuf/reflect/protoreflect\"\n\t\"google.golang.org/protobuf/reflect/protoregistry\"\n\t\"google.golang.org/protobuf/types/known/durationpb\"\n\t\"google.golang.org/protobuf/types/known/fieldmaskpb\"\n\t\"google.golang.org/protobuf/types/known/structpb\"\n\t\"google.golang.org/protobuf/types/known/timestamppb\"\n\t\"google.golang.org/protobuf/types/known/wrapperspb\"\n)\n\nconst fieldSeparator = \".\"\n\nvar errInvalidFormatMapKey = errors.New(\"invalid formatting for map key\")\n\n// DecodeValues decode url value into proto message.\nfunc DecodeValues(msg proto.Message, values url.Values) error {\n\tfor key, values := range values {\n\t\tif err := populateFieldValues(msg.ProtoReflect(), strings.Split(key, \".\"), values); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc populateFieldValues(v protoreflect.Message, fieldPath []string, values []string) error {\n\tif len(fieldPath) < 1 {\n\t\treturn errors.New(\"no field path\")\n\t}\n\tif len(values) < 1 {\n\t\treturn errors.New(\"no value provided\")\n\t}\n\n\tvar fd protoreflect.FieldDescriptor\n\tfor i, fieldName := range fieldPath {\n\t\tif fd = getFieldDescriptor(v, fieldName); fd == nil {\n\t\t\t// ignore unexpected field.\n\t\t\treturn nil\n\t\t}\n\t\tif fd.IsMap() && len(fieldPath) == 2 {\n\t\t\treturn populateMapField(fd, v.Mutable(fd).Map(), fieldPath, values)\n\t\t}\n\t\tif i == len(fieldPath)-1 {\n\t\t\tbreak\n\t\t}\n\t\tif fd.Message() == nil || fd.Cardinality() == protoreflect.Repeated {\n\t\t\tif fd.IsMap() && len(fieldPath) > 1 {\n\t\t\t\t// post subfield\n\t\t\t\treturn populateMapField(fd, v.Mutable(fd).Map(), []string{fieldPath[1]}, values)\n\t\t\t}\n\t\t\treturn fmt.Errorf(\"invalid path: %q is not a message\", fieldName)\n\t\t}\n\n\t\tv = v.Mutable(fd).Message()\n\t}\n\tif of := fd.ContainingOneof(); of != nil {\n\t\tif f := v.WhichOneof(of); f != nil {\n\t\t\treturn fmt.Errorf(\"field already set for oneof %q\", of.FullName().Name())\n\t\t}\n\t}\n\tswitch {\n\tcase fd.IsList():\n\t\treturn populateRepeatedField(fd, v.Mutable(fd).List(), values)\n\tcase fd.IsMap():\n\t\treturn populateMapField(fd, v.Mutable(fd).Map(), fieldPath, values)\n\t}\n\tif len(values) > 1 {\n\t\treturn fmt.Errorf(\"too many values for field %q: %s\", fd.FullName().Name(), strings.Join(values, \", \"))\n\t}\n\treturn populateField(fd, v, values[0])\n}\n\nfunc getFieldDescriptor(v protoreflect.Message, fieldName string) protoreflect.FieldDescriptor {\n\tvar (\n\t\tfields = v.Descriptor().Fields()\n\t\tfd     = getDescriptorByFieldAndName(fields, fieldName)\n\t)\n\tif fd == nil {\n\t\tswitch {\n\t\tcase v.Descriptor().FullName() == structMessageFullname:\n\t\t\tfd = fields.ByNumber(structFieldsFieldNumber)\n\t\tcase len(fieldName) > 2 && strings.HasSuffix(fieldName, \"[]\"):\n\t\t\tfd = getDescriptorByFieldAndName(fields, strings.TrimSuffix(fieldName, \"[]\"))\n\t\tdefault:\n\t\t\t// If the type is map, you get the string \"map[kratos]\", where \"map\" is a field of proto and \"kratos\" is a key of map\n\t\t\t// Use symbol . for separating fields/structs. (eg. structfield.field)\n\t\t\t// ref: https://github.com/go-playground/form\n\t\t\tfield, _, err := parseURLQueryMapKey(fieldName)\n\t\t\tif err != nil {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tfd = getDescriptorByFieldAndName(fields, field)\n\t\t}\n\t}\n\treturn fd\n}\n\nfunc getDescriptorByFieldAndName(fields protoreflect.FieldDescriptors, fieldName string) protoreflect.FieldDescriptor {\n\tvar fd protoreflect.FieldDescriptor\n\tif fd = fields.ByName(protoreflect.Name(fieldName)); fd == nil {\n\t\tfd = fields.ByJSONName(fieldName)\n\t}\n\treturn fd\n}\n\nfunc populateField(fd protoreflect.FieldDescriptor, v protoreflect.Message, value string) error {\n\tif value == \"\" {\n\t\treturn nil\n\t}\n\tval, err := parseField(fd, value)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"parsing field %q: %w\", fd.FullName().Name(), err)\n\t}\n\tv.Set(fd, val)\n\treturn nil\n}\n\nfunc populateRepeatedField(fd protoreflect.FieldDescriptor, list protoreflect.List, values []string) error {\n\tfor _, value := range values {\n\t\tv, err := parseField(fd, value)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"parsing list %q: %w\", fd.FullName().Name(), err)\n\t\t}\n\t\tlist.Append(v)\n\t}\n\treturn nil\n}\n\nfunc populateMapField(fd protoreflect.FieldDescriptor, mp protoreflect.Map, fieldPath []string, values []string) error {\n\t_, keyName, err := parseURLQueryMapKey(strings.Join(fieldPath, fieldSeparator))\n\tif err != nil {\n\t\treturn err\n\t}\n\tkey, err := parseField(fd.MapKey(), keyName)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"parsing map key %q: %w\", fd.FullName().Name(), err)\n\t}\n\tvalue, err := parseField(fd.MapValue(), values[len(values)-1])\n\tif err != nil {\n\t\treturn fmt.Errorf(\"parsing map value %q: %w\", fd.FullName().Name(), err)\n\t}\n\tmp.Set(key.MapKey(), value)\n\treturn nil\n}\n\nfunc parseField(fd protoreflect.FieldDescriptor, value string) (protoreflect.Value, error) {\n\tswitch fd.Kind() {\n\tcase protoreflect.BoolKind:\n\t\tv, err := strconv.ParseBool(value)\n\t\tif err != nil {\n\t\t\treturn protoreflect.Value{}, err\n\t\t}\n\t\treturn protoreflect.ValueOfBool(v), nil\n\tcase protoreflect.EnumKind:\n\t\tenum, err := protoregistry.GlobalTypes.FindEnumByName(fd.Enum().FullName())\n\t\tswitch {\n\t\tcase errors.Is(err, protoregistry.NotFound):\n\t\t\treturn protoreflect.Value{}, fmt.Errorf(\"enum %q is not registered\", fd.Enum().FullName())\n\t\tcase err != nil:\n\t\t\treturn protoreflect.Value{}, fmt.Errorf(\"failed to look up enum: %w\", err)\n\t\t}\n\t\tv := enum.Descriptor().Values().ByName(protoreflect.Name(value))\n\t\tif v == nil {\n\t\t\ti, err := strconv.ParseInt(value, 10, 32) //nolint:mnd\n\t\t\tif err != nil {\n\t\t\t\treturn protoreflect.Value{}, fmt.Errorf(\"%q is not a valid value\", value)\n\t\t\t}\n\t\t\tv = enum.Descriptor().Values().ByNumber(protoreflect.EnumNumber(i))\n\t\t\tif v == nil {\n\t\t\t\treturn protoreflect.Value{}, fmt.Errorf(\"%q is not a valid value\", value)\n\t\t\t}\n\t\t}\n\t\treturn protoreflect.ValueOfEnum(v.Number()), nil\n\tcase protoreflect.Int32Kind, protoreflect.Sint32Kind, protoreflect.Sfixed32Kind:\n\t\tv, err := strconv.ParseInt(value, 10, 32) //nolint:mnd\n\t\tif err != nil {\n\t\t\treturn protoreflect.Value{}, err\n\t\t}\n\t\treturn protoreflect.ValueOfInt32(int32(v)), nil\n\tcase protoreflect.Int64Kind, protoreflect.Sint64Kind, protoreflect.Sfixed64Kind:\n\t\tv, err := strconv.ParseInt(value, 10, 64) //nolint:mnd\n\t\tif err != nil {\n\t\t\treturn protoreflect.Value{}, err\n\t\t}\n\t\treturn protoreflect.ValueOfInt64(v), nil\n\tcase protoreflect.Uint32Kind, protoreflect.Fixed32Kind:\n\t\tv, err := strconv.ParseUint(value, 10, 32) //nolint:mnd\n\t\tif err != nil {\n\t\t\treturn protoreflect.Value{}, err\n\t\t}\n\t\treturn protoreflect.ValueOfUint32(uint32(v)), nil\n\tcase protoreflect.Uint64Kind, protoreflect.Fixed64Kind:\n\t\tv, err := strconv.ParseUint(value, 10, 64) //nolint:mnd\n\t\tif err != nil {\n\t\t\treturn protoreflect.Value{}, err\n\t\t}\n\t\treturn protoreflect.ValueOfUint64(v), nil\n\tcase protoreflect.FloatKind:\n\t\tv, err := strconv.ParseFloat(value, 32) //nolint:mnd\n\t\tif err != nil {\n\t\t\treturn protoreflect.Value{}, err\n\t\t}\n\t\treturn protoreflect.ValueOfFloat32(float32(v)), nil\n\tcase protoreflect.DoubleKind:\n\t\tv, err := strconv.ParseFloat(value, 64) //nolint:mnd\n\t\tif err != nil {\n\t\t\treturn protoreflect.Value{}, err\n\t\t}\n\t\treturn protoreflect.ValueOfFloat64(v), nil\n\tcase protoreflect.StringKind:\n\t\treturn protoreflect.ValueOfString(value), nil\n\tcase protoreflect.BytesKind:\n\t\tv, err := base64.StdEncoding.DecodeString(value)\n\t\tif err != nil {\n\t\t\treturn protoreflect.Value{}, err\n\t\t}\n\t\treturn protoreflect.ValueOfBytes(v), nil\n\tcase protoreflect.MessageKind, protoreflect.GroupKind:\n\t\treturn parseMessage(fd.Message(), value)\n\tdefault:\n\t\tpanic(fmt.Sprintf(\"unknown field kind: %v\", fd.Kind()))\n\t}\n}\n\nfunc parseMessage(md protoreflect.MessageDescriptor, value string) (protoreflect.Value, error) {\n\tvar msg proto.Message\n\tswitch md.FullName() {\n\tcase \"google.protobuf.Timestamp\":\n\t\tif value == nullStr {\n\t\t\tbreak\n\t\t}\n\t\tt, err := time.ParseInLocation(time.RFC3339Nano, value, time.Local)\n\t\tif err != nil {\n\t\t\treturn protoreflect.Value{}, err\n\t\t}\n\t\tmsg = timestamppb.New(t)\n\tcase \"google.protobuf.Duration\":\n\t\tif value == nullStr {\n\t\t\tbreak\n\t\t}\n\t\td, err := time.ParseDuration(value)\n\t\tif err != nil {\n\t\t\treturn protoreflect.Value{}, err\n\t\t}\n\t\tmsg = durationpb.New(d)\n\tcase \"google.protobuf.DoubleValue\":\n\t\tv, err := strconv.ParseFloat(value, 64) //nolint:mnd\n\t\tif err != nil {\n\t\t\treturn protoreflect.Value{}, err\n\t\t}\n\t\tmsg = wrapperspb.Double(v)\n\tcase \"google.protobuf.FloatValue\":\n\t\tv, err := strconv.ParseFloat(value, 32) //nolint:mnd\n\t\tif err != nil {\n\t\t\treturn protoreflect.Value{}, err\n\t\t}\n\t\tmsg = wrapperspb.Float(float32(v))\n\tcase \"google.protobuf.Int64Value\":\n\t\tv, err := strconv.ParseInt(value, 10, 64) //nolint:mnd\n\t\tif err != nil {\n\t\t\treturn protoreflect.Value{}, err\n\t\t}\n\t\tmsg = wrapperspb.Int64(v)\n\tcase \"google.protobuf.Int32Value\":\n\t\tv, err := strconv.ParseInt(value, 10, 32) //nolint:mnd\n\t\tif err != nil {\n\t\t\treturn protoreflect.Value{}, err\n\t\t}\n\t\tmsg = wrapperspb.Int32(int32(v))\n\tcase \"google.protobuf.UInt64Value\":\n\t\tv, err := strconv.ParseUint(value, 10, 64) //nolint:mnd\n\t\tif err != nil {\n\t\t\treturn protoreflect.Value{}, err\n\t\t}\n\t\tmsg = wrapperspb.UInt64(v)\n\tcase \"google.protobuf.UInt32Value\":\n\t\tv, err := strconv.ParseUint(value, 10, 32) //nolint:mnd\n\t\tif err != nil {\n\t\t\treturn protoreflect.Value{}, err\n\t\t}\n\t\tmsg = wrapperspb.UInt32(uint32(v))\n\tcase \"google.protobuf.BoolValue\":\n\t\tv, err := strconv.ParseBool(value)\n\t\tif err != nil {\n\t\t\treturn protoreflect.Value{}, err\n\t\t}\n\t\tmsg = wrapperspb.Bool(v)\n\tcase \"google.protobuf.StringValue\":\n\t\tmsg = wrapperspb.String(value)\n\tcase \"google.protobuf.BytesValue\":\n\t\tv, err := base64.StdEncoding.DecodeString(value)\n\t\tif err != nil {\n\t\t\tif v, err = base64.URLEncoding.DecodeString(value); err != nil {\n\t\t\t\treturn protoreflect.Value{}, err\n\t\t\t}\n\t\t}\n\t\tmsg = wrapperspb.Bytes(v)\n\tcase \"google.protobuf.FieldMask\":\n\t\tfm := &fieldmaskpb.FieldMask{}\n\t\tfor _, fv := range strings.Split(value, \",\") {\n\t\t\tfm.Paths = append(fm.Paths, jsonSnakeCase(fv))\n\t\t}\n\t\tmsg = fm\n\tcase \"google.protobuf.Value\":\n\t\tfm, err := structpb.NewValue(value)\n\t\tif err != nil {\n\t\t\treturn protoreflect.Value{}, err\n\t\t}\n\t\tmsg = fm\n\tcase \"google.protobuf.Struct\":\n\t\tvar v structpb.Struct\n\t\tif err := protojson.Unmarshal([]byte(value), &v); err != nil {\n\t\t\treturn protoreflect.Value{}, err\n\t\t}\n\t\tmsg = &v\n\tdefault:\n\t\treturn protoreflect.Value{}, fmt.Errorf(\"unsupported message type: %q\", string(md.FullName()))\n\t}\n\treturn protoreflect.ValueOfMessage(msg.ProtoReflect()), nil\n}\n\n// jsonSnakeCase converts a camelCase identifier to a snake_case identifier,\n// according to the protobuf JSON specification.\n// references: https://github.com/protocolbuffers/protobuf-go/blob/master/encoding/protojson/well_known_types.go#L864\nfunc jsonSnakeCase(s string) string {\n\tvar builder strings.Builder\n\tbuilder.Grow(len(s))\n\n\tfor i := 0; i < len(s); i++ { // proto identifiers are always ASCII\n\t\tc := s[i]\n\t\tif isASCIIUpper(c) {\n\t\t\tbuilder.WriteByte('_')\n\t\t\tc += 'a' - 'A' // convert to lowercase\n\t\t}\n\t\tbuilder.WriteByte(c)\n\t}\n\n\treturn builder.String()\n}\n\nfunc isASCIIUpper(c byte) bool {\n\treturn 'A' <= c && c <= 'Z'\n}\n\n// parseURLQueryMapKey parse the url.Values the field name and key name of the value map type key\n// for example: convert \"map[key]\" to \"map\" and \"key\"\nfunc parseURLQueryMapKey(key string) (string, string, error) {\n\tstartIndex := strings.IndexByte(key, '[')\n\tendIndex := strings.IndexByte(key, ']')\n\n\tif startIndex < 0 {\n\t\tfsCount := strings.Count(key, fieldSeparator)\n\t\tif fsCount != 1 {\n\t\t\treturn \"\", \"\", errInvalidFormatMapKey\n\t\t}\n\n\t\tm, k, _ := strings.Cut(key, fieldSeparator)\n\t\tif m == \"\" {\n\t\t\treturn \"\", \"\", errInvalidFormatMapKey\n\t\t}\n\n\t\treturn m, k, nil\n\t}\n\n\tif startIndex <= 0 || startIndex >= endIndex || len(key) != endIndex+1 {\n\t\treturn \"\", \"\", errInvalidFormatMapKey\n\t}\n\n\treturn key[:startIndex], key[startIndex+1 : endIndex], nil\n}\n"
  },
  {
    "path": "encoding/form/proto_decode_test.go",
    "content": "package form\n\nimport (\n\t\"fmt\"\n\t\"net/url\"\n\t\"reflect\"\n\t\"strconv\"\n\t\"testing\"\n\n\t\"google.golang.org/protobuf/reflect/protoreflect\"\n\n\t\"github.com/go-kratos/kratos/v2/internal/testdata/complex\"\n)\n\nfunc TestDecodeValues(t *testing.T) {\n\tform, err := url.ParseQuery(\"a=19&age=18&b=true&bool=false&byte=MTIz&bytes=MTIz&count=3&d=22.22&double=12.33&duration=\" +\n\t\t\"2m0.000000022s&field=1%2C2&float=12.34&id=2233&int32=32&int64=64&numberOne=2233&price=11.23&sex=woman&strings=3344&\" +\n\t\t\"strings=5566&string=go-kratos&timestamp=1970-01-01T00%3A00%3A20.000000002Z&uint32=32&uint64=64&very_simple.component=5566\")\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tcomp := &complex.Complex{}\n\terr = DecodeValues(comp, form)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif comp.Id != int64(2233) {\n\t\tt.Errorf(\"want %v, got %v\", int64(2233), comp.Id)\n\t}\n\tif comp.NoOne != \"2233\" {\n\t\tt.Errorf(\"want %v, got %v\", \"2233\", comp.NoOne)\n\t}\n\tif comp.Simple == nil {\n\t\tt.Fatalf(\"want %v, got %v\", nil, comp.Simple)\n\t}\n\tif comp.Simple.Component != \"5566\" {\n\t\tt.Errorf(\"want %v, got %v\", \"5566\", comp.Simple.Component)\n\t}\n\tif len(comp.Strings) != 2 {\n\t\tt.Fatalf(\"want %v, got %v\", 2, len(comp.Strings))\n\t}\n\tif comp.Strings[0] != \"3344\" {\n\t\tt.Errorf(\"want %v, got %v\", \"3344\", comp.Strings[0])\n\t}\n\tif comp.Strings[1] != \"5566\" {\n\t\tt.Errorf(\"want %v, got %v\", \"5566\", comp.Strings[1])\n\t}\n}\n\nfunc TestGetFieldDescriptor(t *testing.T) {\n\tcomp := &complex.Complex{}\n\n\tfield := getFieldDescriptor(comp.ProtoReflect(), \"id\")\n\tif field.Kind() != protoreflect.Int64Kind {\n\t\tt.Errorf(\"want: %d, got: %d\", protoreflect.Int64Kind, field.Kind())\n\t}\n\n\tfield = getFieldDescriptor(comp.ProtoReflect(), \"strings\")\n\tif field.Kind() != protoreflect.StringKind {\n\t\tt.Errorf(\"want: %d, got: %d\", protoreflect.StringKind, field.Kind())\n\t}\n}\n\nfunc TestPopulateRepeatedField(t *testing.T) {\n\tquery, err := url.ParseQuery(\"strings=3344&strings=5566\")\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tcomp := &complex.Complex{}\n\tfield := getFieldDescriptor(comp.ProtoReflect(), \"strings\")\n\n\terr = populateRepeatedField(field, comp.ProtoReflect().Mutable(field).List(), query[\"strings\"])\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif !reflect.DeepEqual([]string{\"3344\", \"5566\"}, comp.GetStrings()) {\n\t\tt.Errorf(\"want: %v, got: %v\", []string{\"3344\", \"5566\"}, comp.GetStrings())\n\t}\n}\n\nfunc TestPopulateMapField(t *testing.T) {\n\tquery, err := url.ParseQuery(\"map%5Bkratos%5D=https://go-kratos.dev/\")\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tcomp := &complex.Complex{}\n\tfield := getFieldDescriptor(comp.ProtoReflect(), \"map\")\n\t// Fill the comp map field with the url query values\n\terr = populateMapField(field, comp.ProtoReflect().Mutable(field).Map(), []string{\"map[kratos]\"}, query[\"map[kratos]\"])\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\t// Get the comp map field value\n\tif query[\"map[kratos]\"][0] != comp.Map[\"kratos\"] {\n\t\tt.Errorf(\"want: %s, got: %s\", query[\"map[kratos]\"], comp.Map[\"kratos\"])\n\t}\n}\n\nfunc TestPopulateMapSepField(t *testing.T) {\n\tquery, err := url.ParseQuery(\"map.name=kratos\")\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tcomp := &complex.Complex{}\n\tfield := getFieldDescriptor(comp.ProtoReflect(), \"map\")\n\t// Fill the comp map field with the url query values\n\terr = populateMapField(field, comp.ProtoReflect().Mutable(field).Map(), []string{\"map.name\"}, query[\"map.name\"])\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\t// Get the comp map field value\n\tif query[\"map.name\"][0] != comp.Map[\"name\"] {\n\t\tt.Errorf(\"want: %s, got: %s\", query, comp.Map)\n\t}\n}\n\nfunc TestParseField(t *testing.T) {\n\ttests := []struct {\n\t\tname                    string\n\t\tfieldName               string\n\t\tprotoReflectKind        protoreflect.Kind\n\t\tvalue                   string\n\t\ttargetProtoReflectValue protoreflect.Value\n\t\ttargetErr               error\n\t}{\n\t\t{\n\t\t\tname:                    \"BoolKind\",\n\t\t\tfieldName:               \"b\",\n\t\t\tprotoReflectKind:        protoreflect.BoolKind,\n\t\t\tvalue:                   \"true\",\n\t\t\ttargetProtoReflectValue: protoreflect.ValueOfBool(true),\n\t\t\ttargetErr:               nil,\n\t\t},\n\t\t{\n\t\t\tname:                    \"BoolKind\",\n\t\t\tfieldName:               \"b\",\n\t\t\tprotoReflectKind:        protoreflect.BoolKind,\n\t\t\tvalue:                   \"a\",\n\t\t\ttargetProtoReflectValue: protoreflect.Value{},\n\t\t\ttargetErr:               &strconv.NumError{Func: \"ParseBool\", Num: \"a\", Err: strconv.ErrSyntax},\n\t\t},\n\t\t{\n\t\t\tname:                    \"EnumKind\",\n\t\t\tfieldName:               \"sex\",\n\t\t\tprotoReflectKind:        protoreflect.EnumKind,\n\t\t\tvalue:                   \"1\",\n\t\t\ttargetProtoReflectValue: protoreflect.ValueOfEnum(1),\n\t\t\ttargetErr:               nil,\n\t\t},\n\t\t{\n\t\t\tname:                    \"EnumKind\",\n\t\t\tfieldName:               \"sex\",\n\t\t\tprotoReflectKind:        protoreflect.EnumKind,\n\t\t\tvalue:                   \"2\",\n\t\t\ttargetProtoReflectValue: protoreflect.Value{},\n\t\t\ttargetErr:               fmt.Errorf(\"%q is not a valid value\", \"2\"),\n\t\t},\n\t}\n\tfor _, test := range tests {\n\t\tt.Run(test.name, func(t *testing.T) {\n\t\t\tcomp := &complex.Complex{}\n\t\t\tfield := getFieldDescriptor(comp.ProtoReflect(), test.fieldName)\n\t\t\tif test.protoReflectKind != field.Kind() {\n\t\t\t\tt.Fatalf(\"want: %d, got: %d\", test.protoReflectKind, field.Kind())\n\t\t\t}\n\t\t\tval, err := parseField(field, test.value)\n\t\t\tif !reflect.DeepEqual(test.targetErr, err) {\n\t\t\t\tt.Fatalf(\"want: %s, got: %s\", test.targetErr, err)\n\t\t\t}\n\t\t\tif !reflect.DeepEqual(test.targetProtoReflectValue, val) {\n\t\t\t\tt.Errorf(\"want: %s, got: %s\", test.targetProtoReflectValue, val)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestJsonSnakeCase(t *testing.T) {\n\ttests := []struct {\n\t\tcamelCase string\n\t\tsnakeCase string\n\t}{\n\t\t{\n\t\t\t\"userId\", \"user_id\",\n\t\t},\n\t\t{\n\t\t\t\"user\", \"user\",\n\t\t},\n\t\t{\n\t\t\t\"userIdAndUsername\", \"user_id_and_username\",\n\t\t},\n\t\t{\n\t\t\t\"\", \"\",\n\t\t},\n\t}\n\tfor _, test := range tests {\n\t\tt.Run(test.camelCase, func(t *testing.T) {\n\t\t\tsnake := jsonSnakeCase(test.camelCase)\n\t\t\tif snake != test.snakeCase {\n\t\t\t\tt.Errorf(\"want: %s, got: %s\", test.snakeCase, snake)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestIsASCIIUpper(t *testing.T) {\n\ttests := []struct {\n\t\tb     byte\n\t\tupper bool\n\t}{\n\t\t{\n\t\t\t'A', true,\n\t\t},\n\t\t{\n\t\t\t'a', false,\n\t\t},\n\t\t{\n\t\t\t',', false,\n\t\t},\n\t\t{\n\t\t\t'1', false,\n\t\t},\n\t\t{\n\t\t\t' ', false,\n\t\t},\n\t}\n\tfor _, test := range tests {\n\t\tt.Run(string(test.b), func(t *testing.T) {\n\t\t\tupper := isASCIIUpper(test.b)\n\t\t\tif test.upper != upper {\n\t\t\t\tt.Errorf(\"'%s' is not ascii upper\", string(test.b))\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestParseURLQueryMapKey(t *testing.T) {\n\ttests := []struct {\n\t\tfieldName string\n\t\tfield     string\n\t\tfieldKey  string\n\t\terr       error\n\t}{\n\t\t{\n\t\t\tfieldName: \"map[kratos]\", field: \"map\", fieldKey: \"kratos\", err: nil,\n\t\t},\n\t\t{\n\t\t\tfieldName: \"map[]\", field: \"map\", fieldKey: \"\", err: nil,\n\t\t},\n\t\t{\n\t\t\tfieldName: \"\", field: \"\", fieldKey: \"\", err: errInvalidFormatMapKey,\n\t\t},\n\t\t{\n\t\t\tfieldName: \"[[]\", field: \"\", fieldKey: \"\", err: errInvalidFormatMapKey,\n\t\t},\n\t\t{\n\t\t\tfieldName: \"map[kratos]=\", field: \"\", fieldKey: \"\", err: errInvalidFormatMapKey,\n\t\t},\n\t\t{\n\t\t\tfieldName: \"[kratos]\", field: \"\", fieldKey: \"\", err: errInvalidFormatMapKey,\n\t\t},\n\t\t{\n\t\t\tfieldName: \"map\", field: \"\", fieldKey: \"\", err: errInvalidFormatMapKey,\n\t\t},\n\t\t{\n\t\t\tfieldName: \"map[\", field: \"\", fieldKey: \"\", err: errInvalidFormatMapKey,\n\t\t},\n\t\t{\n\t\t\tfieldName: \"]kratos[\", field: \"\", fieldKey: \"\", err: errInvalidFormatMapKey,\n\t\t},\n\t\t{\n\t\t\tfieldName: \"[kratos\", field: \"\", fieldKey: \"\", err: errInvalidFormatMapKey,\n\t\t},\n\t\t{\n\t\t\tfieldName: \"kratos]\", field: \"\", fieldKey: \"\", err: errInvalidFormatMapKey,\n\t\t},\n\t\t{\n\t\t\tfieldName: \"map.kratos\", field: \"map\", fieldKey: \"kratos\", err: nil,\n\t\t},\n\t\t{\n\t\t\tfieldName: \"map.\", field: \"map\", fieldKey: \"\", err: nil,\n\t\t},\n\t\t{\n\t\t\tfieldName: \".kratos\", field: \"\", fieldKey: \"\", err: errInvalidFormatMapKey,\n\t\t},\n\t\t{\n\t\t\tfieldName: \"map.kratos.v2\", field: \"\", fieldKey: \"\", err: errInvalidFormatMapKey,\n\t\t},\n\t}\n\tfor _, test := range tests {\n\t\tt.Run(test.fieldName, func(t *testing.T) {\n\t\t\tfieldName, fieldKey, err := parseURLQueryMapKey(test.fieldName)\n\t\t\tif test.err != err {\n\t\t\t\tt.Fatalf(\"want: %s, got: %s\", test.err, err)\n\t\t\t}\n\t\t\tif test.field != fieldName {\n\t\t\t\tt.Errorf(\"want: %s, got: %s\", test.field, fieldName)\n\t\t\t}\n\t\t\tif test.fieldKey != fieldKey {\n\t\t\t\tt.Errorf(\"want: %s, got: %s\", test.fieldKey, fieldKey)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc BenchmarkParseURLQueryMapKey(b *testing.B) {\n\ttestCases := []struct {\n\t\ttestName       string\n\t\tfieldName      string\n\t\twantedField    string\n\t\twantedFieldKey string\n\t\twantedErr      error\n\t}{\n\t\t{\n\t\t\ttestName:       \"with bracket\",\n\t\t\tfieldName:      \"kratos[version]\",\n\t\t\twantedField:    \"kratos\",\n\t\t\twantedFieldKey: \"version\",\n\t\t\twantedErr:      nil,\n\t\t},\n\t\t{\n\t\t\ttestName:       \"with point\",\n\t\t\tfieldName:      \"kratos.version\",\n\t\t\twantedField:    \"kratos\",\n\t\t\twantedFieldKey: \"version\",\n\t\t\twantedErr:      nil,\n\t\t},\n\t}\n\n\tfor _, testCase := range testCases {\n\t\tb.Run(testCase.testName, func(b *testing.B) {\n\t\t\tb.ReportAllocs()\n\t\t\tb.ResetTimer()\n\t\t\tfor i := 0; i < b.N; i++ {\n\t\t\t\tfield, fieldKey, err := parseURLQueryMapKey(testCase.fieldName)\n\t\t\t\tif testCase.wantedErr != err {\n\t\t\t\t\tb.Fatalf(\"want: %s, got: %s\", testCase.wantedErr, err)\n\t\t\t\t}\n\t\t\t\tif testCase.wantedField != field {\n\t\t\t\t\tb.Errorf(\"want: %s, got: %s\", testCase.wantedField, field)\n\t\t\t\t}\n\t\t\t\tif testCase.wantedFieldKey != fieldKey {\n\t\t\t\t\tb.Errorf(\"want: %s, got: %s\", testCase.wantedFieldKey, fieldKey)\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "encoding/form/proto_encode.go",
    "content": "package form\n\nimport (\n\t\"encoding/base64\"\n\t\"fmt\"\n\t\"net/url\"\n\t\"reflect\"\n\t\"strconv\"\n\t\"strings\"\n\n\t\"google.golang.org/protobuf/proto\"\n\t\"google.golang.org/protobuf/reflect/protoreflect\"\n\t\"google.golang.org/protobuf/types/known/fieldmaskpb\"\n)\n\n// EncodeValues encode a message into url values.\nfunc EncodeValues(msg any) (url.Values, error) {\n\tif msg == nil || (reflect.ValueOf(msg).Kind() == reflect.Ptr && reflect.ValueOf(msg).IsNil()) {\n\t\treturn url.Values{}, nil\n\t}\n\tif v, ok := msg.(proto.Message); ok {\n\t\tu := make(url.Values)\n\t\terr := encodeByField(u, \"\", v.ProtoReflect())\n\t\treturn u, err\n\t}\n\treturn encoder.Encode(msg)\n}\n\nfunc encodeByField(u url.Values, path string, m protoreflect.Message) (finalErr error) {\n\tm.Range(func(fd protoreflect.FieldDescriptor, v protoreflect.Value) bool {\n\t\tvar (\n\t\t\tkey     string\n\t\t\tnewPath string\n\t\t)\n\t\tif fd.HasJSONName() {\n\t\t\tkey = fd.JSONName()\n\t\t} else {\n\t\t\tkey = fd.TextName()\n\t\t}\n\t\tif path == \"\" {\n\t\t\tnewPath = key\n\t\t} else {\n\t\t\tnewPath = path + \".\" + key\n\t\t}\n\t\tif of := fd.ContainingOneof(); of != nil {\n\t\t\tif f := m.WhichOneof(of); f != nil && f != fd {\n\t\t\t\treturn true\n\t\t\t}\n\t\t}\n\t\tswitch {\n\t\tcase fd.IsList():\n\t\t\tif v.List().Len() > 0 {\n\t\t\t\tlist, err := encodeRepeatedField(fd, v.List())\n\t\t\t\tif err != nil {\n\t\t\t\t\tfinalErr = err\n\t\t\t\t}\n\t\t\t\tfor _, item := range list {\n\t\t\t\t\tu.Add(newPath, item)\n\t\t\t\t}\n\t\t\t}\n\t\tcase fd.IsMap():\n\t\t\tif v.Map().Len() > 0 {\n\t\t\t\tm, err := encodeMapField(fd, v.Map())\n\t\t\t\tif err != nil {\n\t\t\t\t\tfinalErr = err\n\t\t\t\t}\n\t\t\t\tfor k, value := range m {\n\t\t\t\t\tu.Set(newPath+\"[\"+k+\"]\", value)\n\t\t\t\t}\n\t\t\t}\n\t\tcase (fd.Kind() == protoreflect.MessageKind) || (fd.Kind() == protoreflect.GroupKind):\n\t\t\tvalue, err := encodeMessage(fd.Message(), v)\n\t\t\tif err == nil {\n\t\t\t\tu.Set(newPath, value)\n\t\t\t\treturn true\n\t\t\t}\n\t\t\tif err = encodeByField(u, newPath, v.Message()); err != nil {\n\t\t\t\tfinalErr = err\n\t\t\t}\n\t\tdefault:\n\t\t\tvalue, err := EncodeField(fd, v)\n\t\t\tif err != nil {\n\t\t\t\tfinalErr = err\n\t\t\t}\n\t\t\tu.Set(newPath, value)\n\t\t}\n\t\treturn true\n\t})\n\treturn\n}\n\nfunc encodeRepeatedField(fieldDescriptor protoreflect.FieldDescriptor, list protoreflect.List) ([]string, error) {\n\tvar values []string\n\tfor i := 0; i < list.Len(); i++ {\n\t\tvalue, err := EncodeField(fieldDescriptor, list.Get(i))\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tvalues = append(values, value)\n\t}\n\treturn values, nil\n}\n\nfunc encodeMapField(fieldDescriptor protoreflect.FieldDescriptor, mp protoreflect.Map) (map[string]string, error) {\n\tm := make(map[string]string)\n\tmp.Range(func(k protoreflect.MapKey, v protoreflect.Value) bool {\n\t\tkey, err := EncodeField(fieldDescriptor.MapKey(), k.Value())\n\t\tif err != nil {\n\t\t\treturn false\n\t\t}\n\t\tvalue, err := EncodeField(fieldDescriptor.MapValue(), v)\n\t\tif err != nil {\n\t\t\treturn false\n\t\t}\n\t\tm[key] = value\n\t\treturn true\n\t})\n\n\treturn m, nil\n}\n\n// EncodeField encode proto message filed\nfunc EncodeField(fieldDescriptor protoreflect.FieldDescriptor, value protoreflect.Value) (string, error) {\n\tswitch fieldDescriptor.Kind() {\n\tcase protoreflect.BoolKind:\n\t\treturn strconv.FormatBool(value.Bool()), nil\n\tcase protoreflect.EnumKind:\n\t\tif fieldDescriptor.Enum().FullName() == \"google.protobuf.NullValue\" {\n\t\t\treturn nullStr, nil\n\t\t}\n\t\tdesc := fieldDescriptor.Enum().Values().ByNumber(value.Enum())\n\t\treturn string(desc.Name()), nil\n\tcase protoreflect.BytesKind:\n\t\treturn base64.URLEncoding.EncodeToString(value.Bytes()), nil\n\tcase protoreflect.MessageKind, protoreflect.GroupKind:\n\t\treturn encodeMessage(fieldDescriptor.Message(), value)\n\tdefault:\n\t\treturn value.String(), nil\n\t}\n}\n\n// encodeMessage marshals the fields in the given protoreflect.Message.\n// If the typeURL is non-empty, then a synthetic \"@type\" field is injected\n// containing the URL as the value.\nfunc encodeMessage(msgDescriptor protoreflect.MessageDescriptor, value protoreflect.Value) (string, error) {\n\tswitch msgDescriptor.FullName() {\n\tcase timestampMessageFullname:\n\t\treturn marshalTimestamp(value.Message())\n\tcase durationMessageFullname:\n\t\treturn marshalDuration(value.Message())\n\tcase bytesMessageFullname:\n\t\treturn marshalBytes(value.Message())\n\tcase \"google.protobuf.DoubleValue\", \"google.protobuf.FloatValue\", \"google.protobuf.Int64Value\", \"google.protobuf.Int32Value\",\n\t\t\"google.protobuf.UInt64Value\", \"google.protobuf.UInt32Value\", \"google.protobuf.BoolValue\", \"google.protobuf.StringValue\":\n\t\tfd := msgDescriptor.Fields()\n\t\tv := value.Message().Get(fd.ByName(\"value\"))\n\t\treturn fmt.Sprint(v.Interface()), nil\n\tcase fieldMaskFullName:\n\t\tm, ok := value.Message().Interface().(*fieldmaskpb.FieldMask)\n\t\tif !ok || m == nil {\n\t\t\treturn \"\", nil\n\t\t}\n\t\tfor i, v := range m.Paths {\n\t\t\tm.Paths[i] = jsonCamelCase(v)\n\t\t}\n\t\treturn strings.Join(m.Paths, \",\"), nil\n\tdefault:\n\t\treturn \"\", fmt.Errorf(\"unsupported message type: %q\", string(msgDescriptor.FullName()))\n\t}\n}\n\n// EncodeFieldMask return field mask name=paths\nfunc EncodeFieldMask(m protoreflect.Message) (query string) {\n\tm.Range(func(fd protoreflect.FieldDescriptor, v protoreflect.Value) bool {\n\t\tif fd.Kind() == protoreflect.MessageKind {\n\t\t\tif msg := fd.Message(); msg.FullName() == fieldMaskFullName {\n\t\t\t\tvalue, err := encodeMessage(msg, v)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn false\n\t\t\t\t}\n\t\t\t\tif fd.HasJSONName() {\n\t\t\t\t\tquery = fd.JSONName() + \"=\" + value\n\t\t\t\t} else {\n\t\t\t\t\tquery = fd.TextName() + \"=\" + value\n\t\t\t\t}\n\t\t\t\treturn false\n\t\t\t}\n\t\t}\n\t\treturn true\n\t})\n\treturn\n}\n\n// jsonCamelCase converts a snake_case identifier to a camelCase identifier,\n// according to the protobuf JSON specification.\n// references: https://github.com/protocolbuffers/protobuf-go/blob/master/encoding/protojson/well_known_types.go#L842\nfunc jsonCamelCase(s string) string {\n\tvar builder strings.Builder\n\tbuilder.Grow(len(s))\n\n\twasUnderscore := false\n\tfor i := 0; i < len(s); i++ { // proto identifiers are always ASCIIS\n\t\tc := s[i]\n\t\tif c != '_' {\n\t\t\tif wasUnderscore && isASCIILower(c) {\n\t\t\t\tc -= 'a' - 'A' // convert to uppercase\n\t\t\t}\n\t\t\tbuilder.WriteByte(c)\n\t\t}\n\t\twasUnderscore = c == '_'\n\t}\n\n\treturn builder.String()\n}\n\nfunc isASCIILower(c byte) bool {\n\treturn 'a' <= c && c <= 'z'\n}\n"
  },
  {
    "path": "encoding/form/proto_encode_test.go",
    "content": "package form\n\nimport (\n\t\"testing\"\n\t\"time\"\n\n\t\"google.golang.org/protobuf/types/known/durationpb\"\n\t\"google.golang.org/protobuf/types/known/fieldmaskpb\"\n\t\"google.golang.org/protobuf/types/known/timestamppb\"\n\t\"google.golang.org/protobuf/types/known/wrapperspb\"\n\n\t\"github.com/go-kratos/kratos/v2/internal/testdata/complex\"\n)\n\nfunc TestEncodeValues(t *testing.T) {\n\tin := &complex.Complex{\n\t\tId:          2233,\n\t\tNoOne:       \"2233\",\n\t\tSimple:      &complex.Simple{Component: \"5566\"},\n\t\tStrings:     []string{\"3344\", \"5566\"},\n\t\tB:           true,\n\t\tSex:         complex.Sex_woman,\n\t\tAge:         18,\n\t\tA:           19,\n\t\tCount:       3,\n\t\tPrice:       11.23,\n\t\tD:           22.22,\n\t\tByte:        []byte(\"123\"),\n\t\tMap:         map[string]string{\"kratos\": \"https://go-kratos.dev/\", \"kratos_start\": \"https://go-kratos.dev/docs/getting-started/start/\"},\n\t\tMapInt64Key: map[int64]string{1: \"kratos\", 2: \"go-zero\"},\n\n\t\tTimestamp: timestamppb.New(time.Date(1970, 1, 1, 0, 0, 20, 2, time.Local)),\n\t\tDuration:  &durationpb.Duration{Seconds: 120, Nanos: 22},\n\t\tField:     &fieldmaskpb.FieldMask{Paths: []string{\"1\", \"2\"}},\n\t\tDouble:    &wrapperspb.DoubleValue{Value: 12.33},\n\t\tFloat:     &wrapperspb.FloatValue{Value: 12.34},\n\t\tInt64:     &wrapperspb.Int64Value{Value: 64},\n\t\tInt32:     &wrapperspb.Int32Value{Value: 32},\n\t\tUint64:    &wrapperspb.UInt64Value{Value: 64},\n\t\tUint32:    &wrapperspb.UInt32Value{Value: 32},\n\t\tBool:      &wrapperspb.BoolValue{Value: false},\n\t\tString_:   &wrapperspb.StringValue{Value: \"go-kratos\"},\n\t\tBytes:     &wrapperspb.BytesValue{Value: []byte(\"123\")},\n\t}\n\tquery, err := EncodeValues(in)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\twant := \"a=19&age=18&b=true&bool=false&byte=MTIz&bytes=MTIz&count=3&d=22.22&double=12.33&duration=2m0.000000022s&field=1%2C2&float=12.34&id=2233&int32=32&int64=64&map%5Bkratos%5D=https%3A%2F%2Fgo-kratos.dev%2F&map%5Bkratos_start%5D=https%3A%2F%2Fgo-kratos.dev%2Fdocs%2Fgetting-started%2Fstart%2F&map_int64_key%5B1%5D=kratos&map_int64_key%5B2%5D=go-zero&numberOne=2233&price=11.23&sex=woman&string=go-kratos&strings=3344&strings=5566&timestamp=1970-01-01T00%3A00%3A20.000000002Z&uint32=32&uint64=64&very_simple.component=5566\" // nolint:lll\n\tif got := query.Encode(); want != got {\n\t\tt.Errorf(\"\\nwant: %s, \\ngot: %s\", want, got)\n\t}\n}\n\nfunc TestJsonCamelCase(t *testing.T) {\n\ttests := []struct {\n\t\tcamelCase string\n\t\tsnakeCase string\n\t}{\n\t\t{\n\t\t\t\"userId\", \"user_id\",\n\t\t},\n\t\t{\n\t\t\t\"user\", \"user\",\n\t\t},\n\t\t{\n\t\t\t\"userIdAndUsername\", \"user_id_and_username\",\n\t\t},\n\t\t{\n\t\t\t\"\", \"\",\n\t\t},\n\t}\n\tfor _, test := range tests {\n\t\tt.Run(test.snakeCase, func(t *testing.T) {\n\t\t\tcamel := jsonCamelCase(test.snakeCase)\n\t\t\tif camel != test.camelCase {\n\t\t\t\tt.Errorf(\"want: %s, got: %s\", test.camelCase, camel)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestIsASCIILower(t *testing.T) {\n\ttests := []struct {\n\t\tb     byte\n\t\tlower bool\n\t}{\n\t\t{\n\t\t\t'A', false,\n\t\t},\n\t\t{\n\t\t\t'a', true,\n\t\t},\n\t\t{\n\t\t\t',', false,\n\t\t},\n\t\t{\n\t\t\t'1', false,\n\t\t},\n\t\t{\n\t\t\t' ', false,\n\t\t},\n\t}\n\tfor _, test := range tests {\n\t\tt.Run(string(test.b), func(t *testing.T) {\n\t\t\tlower := isASCIILower(test.b)\n\t\t\tif test.lower != lower {\n\t\t\t\tt.Errorf(\"'%s' is not ascii lower\", string(test.b))\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "encoding/form/well_known_types.go",
    "content": "package form\n\nimport (\n\t\"encoding/base64\"\n\t\"fmt\"\n\t\"math\"\n\t\"strings\"\n\t\"time\"\n\n\t\"google.golang.org/protobuf/reflect/protoreflect\"\n)\n\nconst (\n\t// timestamp\n\ttimestampMessageFullname    protoreflect.FullName    = \"google.protobuf.Timestamp\"\n\tmaxTimestampSeconds                                  = 253402300799\n\tminTimestampSeconds                                  = -6213559680013\n\ttimestampSecondsFieldNumber protoreflect.FieldNumber = 1\n\ttimestampNanosFieldNumber   protoreflect.FieldNumber = 2\n\n\t// duration\n\tdurationMessageFullname    protoreflect.FullName    = \"google.protobuf.Duration\"\n\tsecondsInNanos                                      = 999999999\n\tdurationSecondsFieldNumber protoreflect.FieldNumber = 1\n\tdurationNanosFieldNumber   protoreflect.FieldNumber = 2\n\n\t// bytes\n\tbytesMessageFullname  protoreflect.FullName    = \"google.protobuf.BytesValue\"\n\tbytesValueFieldNumber protoreflect.FieldNumber = 1\n\n\t// google.protobuf.Struct.\n\tstructMessageFullname   protoreflect.FullName    = \"google.protobuf.Struct\"\n\tstructFieldsFieldNumber protoreflect.FieldNumber = 1\n\n\tfieldMaskFullName protoreflect.FullName = \"google.protobuf.FieldMask\"\n)\n\nfunc marshalTimestamp(m protoreflect.Message) (string, error) {\n\tfds := m.Descriptor().Fields()\n\tfdSeconds := fds.ByNumber(timestampSecondsFieldNumber)\n\tfdNanos := fds.ByNumber(timestampNanosFieldNumber)\n\n\tsecsVal := m.Get(fdSeconds)\n\tnanosVal := m.Get(fdNanos)\n\tsecs := secsVal.Int()\n\tnanos := nanosVal.Int()\n\tif secs < minTimestampSeconds || secs > maxTimestampSeconds {\n\t\treturn \"\", fmt.Errorf(\"%s: seconds out of range %v\", timestampMessageFullname, secs)\n\t}\n\tif nanos < 0 || nanos > secondsInNanos {\n\t\treturn \"\", fmt.Errorf(\"%s: nanos out of range %v\", timestampMessageFullname, nanos)\n\t}\n\t// Uses RFC 3339, where generated output will be Z-normalized and uses 0, 3,\n\t// 6 or 9 fractional digits.\n\tt := time.Unix(secs, nanos).Local()\n\tx := t.Format(\"2006-01-02T15:04:05.000000000\")\n\tx = strings.TrimSuffix(x, \"000\")\n\tx = strings.TrimSuffix(x, \"000\")\n\tx = strings.TrimSuffix(x, \".000\")\n\treturn x + \"Z\", nil\n}\n\nfunc marshalDuration(m protoreflect.Message) (string, error) {\n\tfds := m.Descriptor().Fields()\n\tfdSeconds := fds.ByNumber(durationSecondsFieldNumber)\n\tfdNanos := fds.ByNumber(durationNanosFieldNumber)\n\n\tsecsVal := m.Get(fdSeconds)\n\tnanosVal := m.Get(fdNanos)\n\tsecs := secsVal.Int()\n\tnanos := nanosVal.Int()\n\td := time.Duration(secs) * time.Second\n\toverflow := d/time.Second != time.Duration(secs)\n\td += time.Duration(nanos) * time.Nanosecond\n\toverflow = overflow || (secs < 0 && nanos < 0 && d > 0)\n\toverflow = overflow || (secs > 0 && nanos > 0 && d < 0)\n\tif overflow {\n\t\tswitch {\n\t\tcase secs < 0:\n\t\t\treturn time.Duration(math.MinInt64).String(), nil\n\t\tcase secs > 0:\n\t\t\treturn time.Duration(math.MaxInt64).String(), nil\n\t\t}\n\t}\n\treturn d.String(), nil\n}\n\nfunc marshalBytes(m protoreflect.Message) (string, error) {\n\tfds := m.Descriptor().Fields()\n\tfdBytes := fds.ByNumber(bytesValueFieldNumber)\n\tbytesVal := m.Get(fdBytes)\n\tval := bytesVal.Bytes()\n\treturn base64.StdEncoding.EncodeToString(val), nil\n}\n"
  },
  {
    "path": "encoding/form/well_known_types_test.go",
    "content": "package form\n\nimport (\n\t\"encoding/base64\"\n\t\"testing\"\n\t\"time\"\n\n\t\"google.golang.org/protobuf/reflect/protoreflect\"\n\t\"google.golang.org/protobuf/types/known/durationpb\"\n\t\"google.golang.org/protobuf/types/known/timestamppb\"\n\t\"google.golang.org/protobuf/types/known/wrapperspb\"\n)\n\nfunc TestMarshalTimeStamp(t *testing.T) {\n\ttests := []struct {\n\t\tinput  *timestamppb.Timestamp\n\t\texpect string\n\t}{\n\t\t{\n\t\t\tinput:  timestamppb.New(time.Date(2022, 1, 2, 3, 4, 5, 6, time.Local)),\n\t\t\texpect: \"2022-01-02T03:04:05.000000006Z\",\n\t\t},\n\t\t{\n\t\t\tinput:  timestamppb.New(time.Date(2022, 13, 1, 13, 61, 61, 100, time.Local)),\n\t\t\texpect: \"2023-01-01T14:02:01.000000100Z\",\n\t\t},\n\t}\n\tfor _, v := range tests {\n\t\tgot, err := marshalTimestamp(v.input.ProtoReflect())\n\t\tif err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\t\tif want := v.expect; got != want {\n\t\t\tt.Errorf(\"expect %v, got %v\", want, got)\n\t\t}\n\t}\n}\n\nfunc TestMarshalDuration(t *testing.T) {\n\ttests := []struct {\n\t\tinput  *durationpb.Duration\n\t\texpect string\n\t}{\n\t\t{\n\t\t\tinput:  durationpb.New(time.Duration(1<<63 - 1)),\n\t\t\texpect: \"2562047h47m16.854775807s\",\n\t\t},\n\t\t{\n\t\t\tinput:  durationpb.New(time.Duration(-1 << 63)),\n\t\t\texpect: \"-2562047h47m16.854775808s\",\n\t\t},\n\t\t{\n\t\t\tinput:  durationpb.New(100 * time.Second),\n\t\t\texpect: \"1m40s\",\n\t\t},\n\t\t{\n\t\t\tinput:  durationpb.New(-100 * time.Second),\n\t\t\texpect: \"-1m40s\",\n\t\t},\n\t}\n\tfor _, v := range tests {\n\t\tgot, err := marshalDuration(v.input.ProtoReflect())\n\t\tif err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\t\tif want := v.expect; got != want {\n\t\t\tt.Errorf(\"expect %s, got %s\", want, got)\n\t\t}\n\t}\n}\n\nfunc TestMarshalBytes(t *testing.T) {\n\ttests := []struct {\n\t\tinput  protoreflect.Message\n\t\texpect string\n\t}{\n\t\t{\n\t\t\tinput:  wrapperspb.Bytes([]byte(\"abc123!?$*&()'-=@~\")).ProtoReflect(),\n\t\t\texpect: base64.StdEncoding.EncodeToString([]byte(\"abc123!?$*&()'-=@~\")),\n\t\t},\n\t\t{\n\t\t\tinput:  wrapperspb.Bytes([]byte(\"kratos\")).ProtoReflect(),\n\t\t\texpect: base64.StdEncoding.EncodeToString([]byte(\"kratos\")),\n\t\t},\n\t}\n\tfor _, v := range tests {\n\t\tgot, err := marshalBytes(v.input)\n\t\tif err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\t\tif want := v.expect; got != want {\n\t\t\tt.Errorf(\"expect %v, got %v\", want, got)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "encoding/json/json.go",
    "content": "package json\n\nimport (\n\t\"encoding/json\"\n\t\"reflect\"\n\n\t\"google.golang.org/protobuf/encoding/protojson\"\n\t\"google.golang.org/protobuf/proto\"\n\n\t\"github.com/go-kratos/kratos/v2/encoding\"\n)\n\n// Name is the name registered for the json codec.\nconst Name = \"json\"\n\nvar (\n\t// MarshalOptions is a configurable JSON format marshaller.\n\tMarshalOptions = protojson.MarshalOptions{\n\t\tEmitUnpopulated: true,\n\t}\n\t// UnmarshalOptions is a configurable JSON format parser.\n\tUnmarshalOptions = protojson.UnmarshalOptions{\n\t\tDiscardUnknown: true,\n\t}\n)\n\nfunc init() {\n\tencoding.RegisterCodec(codec{})\n}\n\n// codec is a Codec implementation with json.\ntype codec struct{}\n\nfunc (codec) Marshal(v any) ([]byte, error) {\n\tswitch m := v.(type) {\n\tcase json.Marshaler:\n\t\treturn m.MarshalJSON()\n\tcase proto.Message:\n\t\treturn MarshalOptions.Marshal(m)\n\tdefault:\n\t\treturn json.Marshal(m)\n\t}\n}\n\nfunc (codec) Unmarshal(data []byte, v any) error {\n\tswitch m := v.(type) {\n\tcase json.Unmarshaler:\n\t\treturn m.UnmarshalJSON(data)\n\tcase proto.Message:\n\t\treturn UnmarshalOptions.Unmarshal(data, m)\n\tdefault:\n\t\trv := reflect.ValueOf(v)\n\t\tfor rv := rv; rv.Kind() == reflect.Ptr; {\n\t\t\tif rv.IsNil() {\n\t\t\t\trv.Set(reflect.New(rv.Type().Elem()))\n\t\t\t}\n\t\t\trv = rv.Elem()\n\t\t}\n\t\tif m, ok := reflect.Indirect(rv).Interface().(proto.Message); ok {\n\t\t\treturn UnmarshalOptions.Unmarshal(data, m)\n\t\t}\n\t\treturn json.Unmarshal(data, m)\n\t}\n}\n\nfunc (codec) Name() string {\n\treturn Name\n}\n"
  },
  {
    "path": "encoding/json/json_test.go",
    "content": "package json\n\nimport (\n\t\"encoding/json\"\n\t\"reflect\"\n\t\"strings\"\n\t\"testing\"\n\n\ttestData \"github.com/go-kratos/kratos/v2/internal/testdata/encoding\"\n)\n\ntype testEmbed struct {\n\tLevel1a int `json:\"a\"`\n\tLevel1b int `json:\"b\"`\n\tLevel1c int `json:\"c\"`\n}\n\ntype testMessage struct {\n\tField1 string     `json:\"a\"`\n\tField2 string     `json:\"b\"`\n\tField3 string     `json:\"c\"`\n\tEmbed  *testEmbed `json:\"embed,omitempty\"`\n}\n\ntype mock struct {\n\tvalue int\n}\n\nconst (\n\tUnknown = iota\n\tGopher\n\tZebra\n)\n\nfunc (a *mock) UnmarshalJSON(b []byte) error {\n\tvar s string\n\tif err := json.Unmarshal(b, &s); err != nil {\n\t\treturn err\n\t}\n\tswitch strings.ToLower(s) {\n\tdefault:\n\t\ta.value = Unknown\n\tcase \"gopher\":\n\t\ta.value = Gopher\n\tcase \"zebra\":\n\t\ta.value = Zebra\n\t}\n\n\treturn nil\n}\n\nfunc (a *mock) MarshalJSON() ([]byte, error) {\n\tvar s string\n\tswitch a.value {\n\tdefault:\n\t\ts = \"unknown\"\n\tcase Gopher:\n\t\ts = \"gopher\"\n\tcase Zebra:\n\t\ts = \"zebra\"\n\t}\n\n\treturn json.Marshal(s)\n}\n\nfunc TestJSON_Marshal(t *testing.T) {\n\ttests := []struct {\n\t\tinput  any\n\t\texpect string\n\t}{\n\t\t{\n\t\t\tinput:  &testMessage{},\n\t\t\texpect: `{\"a\":\"\",\"b\":\"\",\"c\":\"\"}`,\n\t\t},\n\t\t{\n\t\t\tinput:  &testMessage{Field1: \"a\", Field2: \"b\", Field3: \"c\"},\n\t\t\texpect: `{\"a\":\"a\",\"b\":\"b\",\"c\":\"c\"}`,\n\t\t},\n\t\t{\n\t\t\tinput:  &testData.TestModel{Id: 1, Name: \"go-kratos\", Hobby: []string{\"1\", \"2\"}},\n\t\t\texpect: `{\"id\":\"1\",\"name\":\"go-kratos\",\"hobby\":[\"1\",\"2\"],\"attrs\":{}}`,\n\t\t},\n\t\t{\n\t\t\tinput:  &mock{value: Gopher},\n\t\t\texpect: `\"gopher\"`,\n\t\t},\n\t}\n\tfor _, v := range tests {\n\t\tdata, err := (codec{}).Marshal(v.input)\n\t\tif err != nil {\n\t\t\tt.Errorf(\"marshal(%#v): %s\", v.input, err)\n\t\t}\n\t\tif got, want := string(data), v.expect; strings.ReplaceAll(got, \" \", \"\") != want {\n\t\t\tif strings.Contains(want, \"\\n\") {\n\t\t\t\tt.Errorf(\"marshal(%#v):\\nHAVE:\\n%s\\nWANT:\\n%s\", v.input, got, want)\n\t\t\t} else {\n\t\t\t\tt.Errorf(\"marshal(%#v):\\nhave %#q\\nwant %#q\", v.input, got, want)\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc TestJSON_Unmarshal(t *testing.T) {\n\tp := testMessage{}\n\tp2 := testData.TestModel{}\n\tp3 := &testData.TestModel{}\n\tp4 := &mock{}\n\ttests := []struct {\n\t\tinput  string\n\t\texpect any\n\t}{\n\t\t{\n\t\t\tinput:  `{\"a\":\"\",\"b\":\"\",\"c\":\"\"}`,\n\t\t\texpect: &testMessage{},\n\t\t},\n\t\t{\n\t\t\tinput:  `{\"a\":\"a\",\"b\":\"b\",\"c\":\"c\"}`,\n\t\t\texpect: &p,\n\t\t},\n\t\t{\n\t\t\tinput:  `{\"id\":\"1\",\"name\":\"go-kratos\",\"hobby\":[\"1\",\"2\"],\"attrs\":{}}`,\n\t\t\texpect: &p2,\n\t\t},\n\t\t{\n\t\t\tinput:  `{\"id\":1,\"name\":\"go-kratos\",\"hobby\":[\"1\",\"2\"]}`,\n\t\t\texpect: &p3,\n\t\t},\n\t\t{\n\t\t\tinput:  `\"zebra\"`,\n\t\t\texpect: p4,\n\t\t},\n\t}\n\tfor _, v := range tests {\n\t\twant := []byte(v.input)\n\t\terr := (codec{}).Unmarshal(want, v.expect)\n\t\tif err != nil {\n\t\t\tt.Errorf(\"marshal(%#v): %s\", v.input, err)\n\t\t}\n\t\tgot, err := codec{}.Marshal(v.expect)\n\t\tif err != nil {\n\t\t\tt.Errorf(\"marshal(%#v): %s\", v.input, err)\n\t\t}\n\t\tif !reflect.DeepEqual(strings.ReplaceAll(string(got), \" \", \"\"), strings.ReplaceAll(string(want), \" \", \"\")) {\n\t\t\tt.Errorf(\"marshal(%#v):\\nhave %#q\\nwant %#q\", v.input, got, want)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "encoding/proto/proto.go",
    "content": "// Package proto defines the protobuf codec. Importing this package will\n// register the codec.\npackage proto\n\nimport (\n\t\"errors\"\n\t\"reflect\"\n\n\t\"google.golang.org/protobuf/proto\"\n\n\t\"github.com/go-kratos/kratos/v2/encoding\"\n)\n\n// Name is the name registered for the proto compressor.\nconst Name = \"proto\"\n\nfunc init() {\n\tencoding.RegisterCodec(codec{})\n}\n\n// codec is a Codec implementation with protobuf. It is the default codec for Transport.\ntype codec struct{}\n\nfunc (codec) Marshal(v any) ([]byte, error) {\n\treturn proto.Marshal(v.(proto.Message))\n}\n\nfunc (codec) Unmarshal(data []byte, v any) error {\n\tpm, err := getProtoMessage(v)\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn proto.Unmarshal(data, pm)\n}\n\nfunc (codec) Name() string {\n\treturn Name\n}\n\nfunc getProtoMessage(v any) (proto.Message, error) {\n\tif msg, ok := v.(proto.Message); ok {\n\t\treturn msg, nil\n\t}\n\tval := reflect.ValueOf(v)\n\tif val.Kind() != reflect.Ptr {\n\t\treturn nil, errors.New(\"not proto message\")\n\t}\n\n\tval = val.Elem()\n\treturn getProtoMessage(val.Interface())\n}\n"
  },
  {
    "path": "encoding/proto/proto_test.go",
    "content": "package proto\n\nimport (\n\t\"reflect\"\n\t\"testing\"\n\n\ttestData \"github.com/go-kratos/kratos/v2/internal/testdata/encoding\"\n)\n\nfunc TestName(t *testing.T) {\n\tc := new(codec)\n\tif !reflect.DeepEqual(c.Name(), \"proto\") {\n\t\tt.Errorf(\"no expect float_key value: %v, but got: %v\", c.Name(), \"proto\")\n\t}\n}\n\nfunc TestCodec(t *testing.T) {\n\tc := new(codec)\n\n\tmodel := testData.TestModel{\n\t\tId:    1,\n\t\tName:  \"kratos\",\n\t\tHobby: []string{\"study\", \"eat\", \"play\"},\n\t}\n\n\tm, err := c.Marshal(&model)\n\tif err != nil {\n\t\tt.Errorf(\"Marshal() should be nil, but got %s\", err)\n\t}\n\n\tvar res testData.TestModel\n\n\terr = c.Unmarshal(m, &res)\n\tif err != nil {\n\t\tt.Errorf(\"Unmarshal() should be nil, but got %s\", err)\n\t}\n\tif !reflect.DeepEqual(res.Id, model.Id) {\n\t\tt.Errorf(\"ID should be %d, but got %d\", res.Id, model.Id)\n\t}\n\tif !reflect.DeepEqual(res.Name, model.Name) {\n\t\tt.Errorf(\"Name should be %s, but got %s\", res.Name, model.Name)\n\t}\n\tif !reflect.DeepEqual(res.Hobby, model.Hobby) {\n\t\tt.Errorf(\"Hobby should be %s, but got %s\", res.Hobby, model.Hobby)\n\t}\n}\n\nfunc TestCodec2(t *testing.T) {\n\tc := new(codec)\n\n\tmodel := testData.TestModel{\n\t\tId:    1,\n\t\tName:  \"kratos\",\n\t\tHobby: []string{\"study\", \"eat\", \"play\"},\n\t}\n\n\tm, err := c.Marshal(&model)\n\tif err != nil {\n\t\tt.Errorf(\"Marshal() should be nil, but got %s\", err)\n\t}\n\n\tvar res testData.TestModel\n\trp := &res\n\n\terr = c.Unmarshal(m, &rp)\n\tif err != nil {\n\t\tt.Errorf(\"Unmarshal() should be nil, but got %s\", err)\n\t}\n\tif !reflect.DeepEqual(res.Id, model.Id) {\n\t\tt.Errorf(\"ID should be %d, but got %d\", res.Id, model.Id)\n\t}\n\tif !reflect.DeepEqual(res.Name, model.Name) {\n\t\tt.Errorf(\"Name should be %s, but got %s\", res.Name, model.Name)\n\t}\n\tif !reflect.DeepEqual(res.Hobby, model.Hobby) {\n\t\tt.Errorf(\"Hobby should be %s, but got %s\", res.Hobby, model.Hobby)\n\t}\n}\n\nfunc Test_getProtoMessage(t *testing.T) {\n\tp := &testData.TestModel{Id: 1}\n\ttype args struct {\n\t\tv any\n\t}\n\ttests := []struct {\n\t\tname    string\n\t\targs    args\n\t\twantErr bool\n\t}{\n\t\t{name: \"test1\", args: args{v: &testData.TestModel{}}, wantErr: false},\n\t\t{name: \"test2\", args: args{v: testData.TestModel{}}, wantErr: true},\n\t\t{name: \"test3\", args: args{v: &p}, wantErr: false},\n\t\t{name: \"test4\", args: args{v: 1}, wantErr: true},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\t_, err := getProtoMessage(tt.args.v)\n\t\t\tif (err != nil) != tt.wantErr {\n\t\t\t\tt.Errorf(\"getProtoMessage() error = %v, wantErr %v\", err, tt.wantErr)\n\t\t\t\treturn\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "encoding/xml/xml.go",
    "content": "package xml\n\nimport (\n\t\"encoding/xml\"\n\n\t\"github.com/go-kratos/kratos/v2/encoding\"\n)\n\n// Name is the name registered for the xml codec.\nconst Name = \"xml\"\n\nfunc init() {\n\tencoding.RegisterCodec(codec{})\n}\n\n// codec is a Codec implementation with xml.\ntype codec struct{}\n\nfunc (codec) Marshal(v any) ([]byte, error) {\n\treturn xml.Marshal(v)\n}\n\nfunc (codec) Unmarshal(data []byte, v any) error {\n\treturn xml.Unmarshal(data, v)\n}\n\nfunc (codec) Name() string {\n\treturn Name\n}\n"
  },
  {
    "path": "encoding/xml/xml_test.go",
    "content": "package xml\n\nimport (\n\t\"reflect\"\n\t\"strings\"\n\t\"testing\"\n)\n\ntype Plain struct {\n\tV any\n}\n\ntype NestedOrder struct {\n\tXMLName struct{} `xml:\"result\"`\n\tField1  string   `xml:\"parent>c\"`\n\tField2  string   `xml:\"parent>b\"`\n\tField3  string   `xml:\"parent>a\"`\n}\n\nfunc TestCodec_Marshal(t *testing.T) {\n\ttests := []struct {\n\t\tValue     any\n\t\tExpectXML string\n\t}{\n\t\t// Test value types\n\t\t{Value: &Plain{true}, ExpectXML: `<Plain><V>true</V></Plain>`},\n\t\t{Value: &Plain{false}, ExpectXML: `<Plain><V>false</V></Plain>`},\n\t\t{Value: &Plain{42}, ExpectXML: `<Plain><V>42</V></Plain>`},\n\t\t{\n\t\t\tValue: &NestedOrder{Field1: \"C\", Field2: \"B\", Field3: \"A\"},\n\t\t\tExpectXML: `<result>` +\n\t\t\t\t`<parent>` +\n\t\t\t\t`<c>C</c>` +\n\t\t\t\t`<b>B</b>` +\n\t\t\t\t`<a>A</a>` +\n\t\t\t\t`</parent>` +\n\t\t\t\t`</result>`,\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tdata, err := (codec{}).Marshal(tt.Value)\n\t\tif err != nil {\n\t\t\tt.Errorf(\"marshal(%#v): %s\", tt.Value, err)\n\t\t}\n\t\tif got, want := string(data), tt.ExpectXML; got != want {\n\t\t\tif strings.Contains(want, \"\\n\") {\n\t\t\t\tt.Errorf(\"marshal(%#v):\\nHAVE:\\n%s\\nWANT:\\n%s\", tt.Value, got, want)\n\t\t\t} else {\n\t\t\t\tt.Errorf(\"marshal(%#v):\\nhave %#q\\nwant %#q\", tt.Value, got, want)\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc TestCodec_Unmarshal(t *testing.T) {\n\ttests := []struct {\n\t\twant     any\n\t\tInputXML string\n\t}{\n\t\t{\n\t\t\twant: &NestedOrder{Field1: \"C\", Field2: \"B\", Field3: \"A\"},\n\t\t\tInputXML: `<result>` +\n\t\t\t\t`<parent>` +\n\t\t\t\t`<c>C</c>` +\n\t\t\t\t`<b>B</b>` +\n\t\t\t\t`<a>A</a>` +\n\t\t\t\t`</parent>` +\n\t\t\t\t`</result>`,\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tvt := reflect.TypeOf(tt.want)\n\t\tdest := reflect.New(vt.Elem()).Interface()\n\t\tdata := []byte(tt.InputXML)\n\t\terr := (codec{}).Unmarshal(data, dest)\n\t\tif err != nil {\n\t\t\tt.Errorf(\"unmarshal(%#v, %#v): %s\", tt.InputXML, dest, err)\n\t\t}\n\t\tif got, want := dest, tt.want; !reflect.DeepEqual(got, want) {\n\t\t\tt.Errorf(\"unmarshal(%q):\\nhave %#v\\nwant %#v\", tt.InputXML, got, want)\n\t\t}\n\t}\n}\n\nfunc TestCodec_NilUnmarshal(t *testing.T) {\n\ttests := []struct {\n\t\twant     any\n\t\tInputXML string\n\t}{\n\t\t{\n\t\t\twant: &NestedOrder{Field1: \"C\", Field2: \"B\", Field3: \"A\"},\n\t\t\tInputXML: `<result>` +\n\t\t\t\t`<parent>` +\n\t\t\t\t`<c>C</c>` +\n\t\t\t\t`<b>B</b>` +\n\t\t\t\t`<a>A</a>` +\n\t\t\t\t`</parent>` +\n\t\t\t\t`</result>`,\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\ts := struct {\n\t\t\tA string `xml:\"a\"`\n\t\t\tB *NestedOrder\n\t\t}{A: \"a\"}\n\t\tdata := []byte(tt.InputXML)\n\t\terr := (codec{}).Unmarshal(data, &s.B)\n\t\tif err != nil {\n\t\t\tt.Errorf(\"unmarshal(%#v, %#v): %s\", tt.InputXML, s.B, err)\n\t\t}\n\t\tif got, want := s.B, tt.want; !reflect.DeepEqual(got, want) {\n\t\t\tt.Errorf(\"unmarshal(%q):\\nhave %#v\\nwant %#v\", tt.InputXML, got, want)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "encoding/yaml/yaml.go",
    "content": "package yaml\n\nimport (\n\t\"gopkg.in/yaml.v3\"\n\n\t\"github.com/go-kratos/kratos/v2/encoding\"\n)\n\n// Name is the name registered for the yaml codec.\nconst Name = \"yaml\"\n\nfunc init() {\n\tencoding.RegisterCodec(codec{})\n}\n\n// codec is a Codec implementation with yaml.\ntype codec struct{}\n\nfunc (codec) Marshal(v any) ([]byte, error) {\n\treturn yaml.Marshal(v)\n}\n\nfunc (codec) Unmarshal(data []byte, v any) error {\n\treturn yaml.Unmarshal(data, v)\n}\n\nfunc (codec) Name() string {\n\treturn Name\n}\n"
  },
  {
    "path": "encoding/yaml/yaml_test.go",
    "content": "package yaml\n\nimport (\n\t\"math\"\n\t\"reflect\"\n\t\"testing\"\n)\n\nfunc TestCodec_Unmarshal(t *testing.T) {\n\ttests := []struct {\n\t\tdata  string\n\t\tvalue any\n\t}{\n\t\t{\n\t\t\t\"\",\n\t\t\t(*struct{})(nil),\n\t\t},\n\t\t{\n\t\t\t\"{}\", &struct{}{},\n\t\t},\n\t\t{\n\t\t\t\"v: hi\",\n\t\t\tmap[string]string{\"v\": \"hi\"},\n\t\t},\n\t\t{\n\t\t\t\"v: hi\", map[string]any{\"v\": \"hi\"},\n\t\t},\n\t\t{\n\t\t\t\"v: true\",\n\t\t\tmap[string]string{\"v\": \"true\"},\n\t\t},\n\t\t{\n\t\t\t\"v: true\",\n\t\t\tmap[string]any{\"v\": true},\n\t\t},\n\t\t{\n\t\t\t\"v: 10\",\n\t\t\tmap[string]any{\"v\": 10},\n\t\t},\n\t\t{\n\t\t\t\"v: 0b10\",\n\t\t\tmap[string]any{\"v\": 2},\n\t\t},\n\t\t{\n\t\t\t\"v: 0xA\",\n\t\t\tmap[string]any{\"v\": 10},\n\t\t},\n\t\t{\n\t\t\t\"v: 4294967296\",\n\t\t\tmap[string]int64{\"v\": 4294967296},\n\t\t},\n\t\t{\n\t\t\t\"v: 0.1\",\n\t\t\tmap[string]any{\"v\": 0.1},\n\t\t},\n\t\t{\n\t\t\t\"v: .1\",\n\t\t\tmap[string]any{\"v\": 0.1},\n\t\t},\n\t\t{\n\t\t\t\"v: .Inf\",\n\t\t\tmap[string]any{\"v\": math.Inf(+1)},\n\t\t},\n\t\t{\n\t\t\t\"v: -.Inf\",\n\t\t\tmap[string]any{\"v\": math.Inf(-1)},\n\t\t},\n\t\t{\n\t\t\t\"v: -10\",\n\t\t\tmap[string]any{\"v\": -10},\n\t\t},\n\t\t{\n\t\t\t\"v: -.1\",\n\t\t\tmap[string]any{\"v\": -0.1},\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tv := reflect.ValueOf(tt.value).Type()\n\t\tvalue := reflect.New(v)\n\t\terr := (codec{}).Unmarshal([]byte(tt.data), value.Interface())\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"(codec{}).Unmarshal should not return err\")\n\t\t}\n\t}\n\tspec := struct {\n\t\tA string\n\t\tB map[string]any\n\t}{A: \"a\"}\n\terr := (codec{}).Unmarshal([]byte(\"v: hi\"), &spec.B)\n\tif err != nil {\n\t\tt.Fatalf(\"(codec{}).Unmarshal should not return err\")\n\t}\n}\n\nfunc TestCodec_Marshal(t *testing.T) {\n\tvalue := map[string]string{\"v\": \"hi\"}\n\tgot, err := (codec{}).Marshal(value)\n\tif err != nil {\n\t\tt.Fatalf(\"should not return err\")\n\t}\n\tif string(got) != \"v: hi\\n\" {\n\t\tt.Fatalf(\"want \\\"v: hi\\n\\\" return \\\"%s\\\"\", string(got))\n\t}\n}\n"
  },
  {
    "path": "errors/errors.go",
    "content": "package errors\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\n\t\"google.golang.org/genproto/googleapis/rpc/errdetails\"\n\t\"google.golang.org/grpc/status\"\n\n\thttpstatus \"github.com/go-kratos/kratos/v2/transport/http/status\"\n)\n\nconst (\n\t// UnknownCode is unknown code for error info.\n\tUnknownCode = 500\n\t// UnknownReason is unknown reason for error info.\n\tUnknownReason = \"\"\n\t// SupportPackageIsVersion1 this constant should not be referenced by any other code.\n\tSupportPackageIsVersion1 = true\n)\n\n// Error is a status error.\ntype Error struct {\n\tStatus\n\tcause error\n}\n\nfunc (e *Error) Error() string {\n\treturn fmt.Sprintf(\"error: code = %d reason = %s message = %s metadata = %v cause = %v\", e.Code, e.Reason, e.Message, e.Metadata, e.cause)\n}\n\n// Unwrap provides compatibility for Go 1.13 error chains.\nfunc (e *Error) Unwrap() error { return e.cause }\n\n// Is matches each error in the chain with the target value.\nfunc (e *Error) Is(err error) bool {\n\tif se := new(Error); errors.As(err, &se) {\n\t\treturn se.Code == e.Code && se.Reason == e.Reason\n\t}\n\treturn false\n}\n\n// WithCause with the underlying cause of the error.\nfunc (e *Error) WithCause(cause error) *Error {\n\terr := Clone(e)\n\terr.cause = cause\n\treturn err\n}\n\n// WithMetadata with an MD formed by the mapping of key, value.\nfunc (e *Error) WithMetadata(md map[string]string) *Error {\n\terr := Clone(e)\n\terr.Metadata = md\n\treturn err\n}\n\n// GRPCStatus returns the Status represented by se.\nfunc (e *Error) GRPCStatus() *status.Status {\n\ts, _ := status.New(httpstatus.ToGRPCCode(int(e.Code)), e.Message).\n\t\tWithDetails(&errdetails.ErrorInfo{\n\t\t\tReason:   e.Reason,\n\t\t\tMetadata: e.Metadata,\n\t\t})\n\treturn s\n}\n\n// New returns an error object for the code, message.\nfunc New(code int, reason, message string) *Error {\n\treturn &Error{\n\t\tStatus: Status{\n\t\t\tCode:    int32(code),\n\t\t\tMessage: message,\n\t\t\tReason:  reason,\n\t\t},\n\t}\n}\n\n// Newf New(code fmt.Sprintf(format, a...))\nfunc Newf(code int, reason, format string, a ...any) *Error {\n\treturn New(code, reason, fmt.Sprintf(format, a...))\n}\n\n// Errorf returns an error object for the code, message and error info.\nfunc Errorf(code int, reason, format string, a ...any) error {\n\treturn New(code, reason, fmt.Sprintf(format, a...))\n}\n\n// Code returns the http code for an error.\n// It supports wrapped errors.\nfunc Code(err error) int {\n\tif err == nil {\n\t\treturn 200 //nolint:mnd\n\t}\n\treturn int(FromError(err).Code)\n}\n\n// Reason returns the reason for a particular error.\n// It supports wrapped errors.\nfunc Reason(err error) string {\n\tif err == nil {\n\t\treturn UnknownReason\n\t}\n\treturn FromError(err).Reason\n}\n\n// Clone deep clone error to a new error.\nfunc Clone(err *Error) *Error {\n\tif err == nil {\n\t\treturn nil\n\t}\n\tmetadata := make(map[string]string, len(err.Metadata))\n\tfor k, v := range err.Metadata {\n\t\tmetadata[k] = v\n\t}\n\treturn &Error{\n\t\tcause: err.cause,\n\t\tStatus: Status{\n\t\t\tCode:     err.Code,\n\t\t\tReason:   err.Reason,\n\t\t\tMessage:  err.Message,\n\t\t\tMetadata: metadata,\n\t\t},\n\t}\n}\n\n// FromError try to convert an error to *Error.\n// It supports wrapped errors.\nfunc FromError(err error) *Error {\n\tif err == nil {\n\t\treturn nil\n\t}\n\tif se := new(Error); errors.As(err, &se) {\n\t\treturn se\n\t}\n\tgs, ok := status.FromError(err)\n\tif !ok {\n\t\treturn New(UnknownCode, UnknownReason, err.Error())\n\t}\n\tret := New(\n\t\thttpstatus.FromGRPCCode(gs.Code()),\n\t\tUnknownReason,\n\t\tgs.Message(),\n\t)\n\tfor _, detail := range gs.Details() {\n\t\tswitch d := detail.(type) {\n\t\tcase *errdetails.ErrorInfo:\n\t\t\tret.Reason = d.Reason\n\t\t\treturn ret.WithMetadata(d.Metadata)\n\t\t}\n\t}\n\treturn ret\n}\n"
  },
  {
    "path": "errors/errors.pb.go",
    "content": "// Code generated by protoc-gen-go. DO NOT EDIT.\n// versions:\n// \tprotoc-gen-go v1.28.0\n// \tprotoc        v3.19.4\n// source: errors/errors.proto\n\npackage errors\n\nimport (\n\tprotoreflect \"google.golang.org/protobuf/reflect/protoreflect\"\n\tprotoimpl \"google.golang.org/protobuf/runtime/protoimpl\"\n\tdescriptorpb \"google.golang.org/protobuf/types/descriptorpb\"\n\treflect \"reflect\"\n\tsync \"sync\"\n)\n\nconst (\n\t// Verify that this generated code is sufficiently up-to-date.\n\t_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)\n\t// Verify that runtime/protoimpl is sufficiently up-to-date.\n\t_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)\n)\n\ntype Status struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\tCode     int32             `protobuf:\"varint,1,opt,name=code,proto3\" json:\"code,omitempty\"`\n\tReason   string            `protobuf:\"bytes,2,opt,name=reason,proto3\" json:\"reason,omitempty\"`\n\tMessage  string            `protobuf:\"bytes,3,opt,name=message,proto3\" json:\"message,omitempty\"`\n\tMetadata map[string]string `protobuf:\"bytes,4,rep,name=metadata,proto3\" json:\"metadata,omitempty\" protobuf_key:\"bytes,1,opt,name=key,proto3\" protobuf_val:\"bytes,2,opt,name=value,proto3\"`\n}\n\nfunc (x *Status) Reset() {\n\t*x = Status{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_errors_errors_proto_msgTypes[0]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *Status) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*Status) ProtoMessage() {}\n\nfunc (x *Status) ProtoReflect() protoreflect.Message {\n\tmi := &file_errors_errors_proto_msgTypes[0]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use Status.ProtoReflect.Descriptor instead.\nfunc (*Status) Descriptor() ([]byte, []int) {\n\treturn file_errors_errors_proto_rawDescGZIP(), []int{0}\n}\n\nfunc (x *Status) GetCode() int32 {\n\tif x != nil {\n\t\treturn x.Code\n\t}\n\treturn 0\n}\n\nfunc (x *Status) GetReason() string {\n\tif x != nil {\n\t\treturn x.Reason\n\t}\n\treturn \"\"\n}\n\nfunc (x *Status) GetMessage() string {\n\tif x != nil {\n\t\treturn x.Message\n\t}\n\treturn \"\"\n}\n\nfunc (x *Status) GetMetadata() map[string]string {\n\tif x != nil {\n\t\treturn x.Metadata\n\t}\n\treturn nil\n}\n\nvar file_errors_errors_proto_extTypes = []protoimpl.ExtensionInfo{\n\t{\n\t\tExtendedType:  (*descriptorpb.EnumOptions)(nil),\n\t\tExtensionType: (*int32)(nil),\n\t\tField:         1108,\n\t\tName:          \"errors.default_code\",\n\t\tTag:           \"varint,1108,opt,name=default_code\",\n\t\tFilename:      \"errors/errors.proto\",\n\t},\n\t{\n\t\tExtendedType:  (*descriptorpb.EnumValueOptions)(nil),\n\t\tExtensionType: (*int32)(nil),\n\t\tField:         1109,\n\t\tName:          \"errors.code\",\n\t\tTag:           \"varint,1109,opt,name=code\",\n\t\tFilename:      \"errors/errors.proto\",\n\t},\n}\n\n// Extension fields to descriptorpb.EnumOptions.\nvar (\n\t// optional int32 default_code = 1108;\n\tE_DefaultCode = &file_errors_errors_proto_extTypes[0]\n)\n\n// Extension fields to descriptorpb.EnumValueOptions.\nvar (\n\t// optional int32 code = 1109;\n\tE_Code = &file_errors_errors_proto_extTypes[1]\n)\n\nvar File_errors_errors_proto protoreflect.FileDescriptor\n\nvar file_errors_errors_proto_rawDesc = []byte{\n\t0x0a, 0x13, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x2f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x2e,\n\t0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x06, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x1a, 0x20, 0x67,\n\t0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x64,\n\t0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22,\n\t0xc5, 0x01, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x63, 0x6f,\n\t0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x12, 0x16,\n\t0x0a, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06,\n\t0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67,\n\t0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65,\n\t0x12, 0x38, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x04, 0x20, 0x03,\n\t0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x2e, 0x53, 0x74, 0x61, 0x74,\n\t0x75, 0x73, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79,\n\t0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x1a, 0x3b, 0x0a, 0x0d, 0x4d, 0x65,\n\t0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b,\n\t0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a,\n\t0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61,\n\t0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x3a, 0x40, 0x0a, 0x0c, 0x64, 0x65, 0x66, 0x61, 0x75,\n\t0x6c, 0x74, 0x5f, 0x63, 0x6f, 0x64, 0x65, 0x12, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65,\n\t0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6e, 0x75, 0x6d, 0x4f, 0x70,\n\t0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0xd4, 0x08, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0b, 0x64, 0x65,\n\t0x66, 0x61, 0x75, 0x6c, 0x74, 0x43, 0x6f, 0x64, 0x65, 0x3a, 0x36, 0x0a, 0x04, 0x63, 0x6f, 0x64,\n\t0x65, 0x12, 0x21, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,\n\t0x62, 0x75, 0x66, 0x2e, 0x45, 0x6e, 0x75, 0x6d, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x4f, 0x70, 0x74,\n\t0x69, 0x6f, 0x6e, 0x73, 0x18, 0xd5, 0x08, 0x20, 0x01, 0x28, 0x05, 0x52, 0x04, 0x63, 0x6f, 0x64,\n\t0x65, 0x42, 0x59, 0x0a, 0x18, 0x63, 0x6f, 0x6d, 0x2e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e,\n\t0x6b, 0x72, 0x61, 0x74, 0x6f, 0x73, 0x2e, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x50, 0x01, 0x5a,\n\t0x2c, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x6f, 0x2d, 0x6b,\n\t0x72, 0x61, 0x74, 0x6f, 0x73, 0x2f, 0x6b, 0x72, 0x61, 0x74, 0x6f, 0x73, 0x2f, 0x76, 0x32, 0x2f,\n\t0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x3b, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0xa2, 0x02, 0x0c,\n\t0x4b, 0x72, 0x61, 0x74, 0x6f, 0x73, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x62, 0x06, 0x70, 0x72,\n\t0x6f, 0x74, 0x6f, 0x33,\n}\n\nvar (\n\tfile_errors_errors_proto_rawDescOnce sync.Once\n\tfile_errors_errors_proto_rawDescData = file_errors_errors_proto_rawDesc\n)\n\nfunc file_errors_errors_proto_rawDescGZIP() []byte {\n\tfile_errors_errors_proto_rawDescOnce.Do(func() {\n\t\tfile_errors_errors_proto_rawDescData = protoimpl.X.CompressGZIP(file_errors_errors_proto_rawDescData)\n\t})\n\treturn file_errors_errors_proto_rawDescData\n}\n\nvar file_errors_errors_proto_msgTypes = make([]protoimpl.MessageInfo, 2)\nvar file_errors_errors_proto_goTypes = []interface{}{\n\t(*Status)(nil),                        // 0: errors.Status\n\tnil,                                   // 1: errors.Status.MetadataEntry\n\t(*descriptorpb.EnumOptions)(nil),      // 2: google.protobuf.EnumOptions\n\t(*descriptorpb.EnumValueOptions)(nil), // 3: google.protobuf.EnumValueOptions\n}\nvar file_errors_errors_proto_depIdxs = []int32{\n\t1, // 0: errors.Status.metadata:type_name -> errors.Status.MetadataEntry\n\t2, // 1: errors.default_code:extendee -> google.protobuf.EnumOptions\n\t3, // 2: errors.code:extendee -> google.protobuf.EnumValueOptions\n\t3, // [3:3] is the sub-list for method output_type\n\t3, // [3:3] is the sub-list for method input_type\n\t3, // [3:3] is the sub-list for extension type_name\n\t1, // [1:3] is the sub-list for extension extendee\n\t0, // [0:1] is the sub-list for field type_name\n}\n\nfunc init() { file_errors_errors_proto_init() }\nfunc file_errors_errors_proto_init() {\n\tif File_errors_errors_proto != nil {\n\t\treturn\n\t}\n\tif !protoimpl.UnsafeEnabled {\n\t\tfile_errors_errors_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*Status); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t}\n\ttype x struct{}\n\tout := protoimpl.TypeBuilder{\n\t\tFile: protoimpl.DescBuilder{\n\t\t\tGoPackagePath: reflect.TypeOf(x{}).PkgPath(),\n\t\t\tRawDescriptor: file_errors_errors_proto_rawDesc,\n\t\t\tNumEnums:      0,\n\t\t\tNumMessages:   2,\n\t\t\tNumExtensions: 2,\n\t\t\tNumServices:   0,\n\t\t},\n\t\tGoTypes:           file_errors_errors_proto_goTypes,\n\t\tDependencyIndexes: file_errors_errors_proto_depIdxs,\n\t\tMessageInfos:      file_errors_errors_proto_msgTypes,\n\t\tExtensionInfos:    file_errors_errors_proto_extTypes,\n\t}.Build()\n\tFile_errors_errors_proto = out.File\n\tfile_errors_errors_proto_rawDesc = nil\n\tfile_errors_errors_proto_goTypes = nil\n\tfile_errors_errors_proto_depIdxs = nil\n}\n"
  },
  {
    "path": "errors/errors.proto",
    "content": "syntax = \"proto3\";\n\npackage errors;\n\noption go_package = \"github.com/go-kratos/kratos/v2/errors;errors\";\noption java_multiple_files = true;\noption java_package = \"com.github.kratos.errors\";\noption objc_class_prefix = \"KratosErrors\";\n\nimport \"google/protobuf/descriptor.proto\";\n\nmessage Status {\n  int32 code = 1;\n  string reason = 2;\n  string message = 3;\n  map<string, string> metadata = 4;\n};\n\nextend google.protobuf.EnumOptions {\n  int32 default_code = 1108;\n}\n\nextend google.protobuf.EnumValueOptions {\n  int32 code = 1109;\n}\n"
  },
  {
    "path": "errors/errors_test.go",
    "content": "package errors\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"reflect\"\n\t\"testing\"\n\n\t\"google.golang.org/grpc/codes\"\n\t\"google.golang.org/grpc/status\"\n)\n\ntype TestError struct{ message string }\n\nfunc (e *TestError) Error() string { return e.message }\n\nfunc TestErrors(t *testing.T) {\n\tvar base *Error\n\terr := Newf(http.StatusBadRequest, \"reason\", \"message\")\n\terr2 := Newf(http.StatusBadRequest, \"reason\", \"message\")\n\terr3 := err.WithMetadata(map[string]string{\n\t\t\"foo\": \"bar\",\n\t})\n\twerr := fmt.Errorf(\"wrap %w\", err)\n\n\tif errors.Is(err, new(Error)) {\n\t\tt.Errorf(\"should not be equal: %v\", err)\n\t}\n\tif !errors.Is(werr, err) {\n\t\tt.Errorf(\"should be equal: %v\", err)\n\t}\n\tif !errors.Is(werr, err2) {\n\t\tt.Errorf(\"should be equal: %v\", err)\n\t}\n\n\tif !errors.As(err, &base) {\n\t\tt.Errorf(\"should be matches: %v\", err)\n\t}\n\tif !IsBadRequest(err) {\n\t\tt.Errorf(\"should be matches: %v\", err)\n\t}\n\n\tif reason := Reason(err); reason != err3.Reason {\n\t\tt.Errorf(\"got %s want: %s\", reason, err)\n\t}\n\n\tif err3.Metadata[\"foo\"] != \"bar\" {\n\t\tt.Error(\"not expected metadata\")\n\t}\n\n\tgs := err.GRPCStatus()\n\tse := FromError(gs.Err())\n\tif se.Reason != \"reason\" {\n\t\tt.Errorf(\"got %+v want %+v\", se, err)\n\t}\n\n\tgs2 := status.New(codes.InvalidArgument, \"bad request\")\n\tse2 := FromError(gs2.Err())\n\t// codes.InvalidArgument should convert to http.StatusBadRequest\n\tif se2.Code != http.StatusBadRequest {\n\t\tt.Errorf(\"convert code err, got %d want %d\", UnknownCode, http.StatusBadRequest)\n\t}\n\tif FromError(nil) != nil {\n\t\tt.Errorf(\"FromError(nil) should be nil\")\n\t}\n\te := FromError(errors.New(\"test\"))\n\tif !reflect.DeepEqual(e.Code, int32(UnknownCode)) {\n\t\tt.Errorf(\"no expect value: %v, but got: %v\", e.Code, int32(UnknownCode))\n\t}\n}\n\nfunc TestIs(t *testing.T) {\n\ttests := []struct {\n\t\tname string\n\t\te    *Error\n\t\terr  error\n\t\twant bool\n\t}{\n\t\t{\n\t\t\tname: \"true\",\n\t\t\te:    New(404, \"test\", \"\"),\n\t\t\terr:  New(http.StatusNotFound, \"test\", \"\"),\n\t\t\twant: true,\n\t\t},\n\t\t{\n\t\t\tname: \"false\",\n\t\t\te:    New(0, \"test\", \"\"),\n\t\t\terr:  errors.New(\"test\"),\n\t\t\twant: false,\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tif ok := tt.e.Is(tt.err); ok != tt.want {\n\t\t\t\tt.Errorf(\"Error.Error() = %v, want %v\", ok, tt.want)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestCause(t *testing.T) {\n\ttestError := &TestError{message: \"test\"}\n\terr := BadRequest(\"foo\", \"bar\").WithCause(testError)\n\tif !errors.Is(err, testError) {\n\t\tt.Fatalf(\"want %v but got %v\", testError, err)\n\t}\n\tif te := new(TestError); errors.As(err, &te) {\n\t\tif te.message != testError.message {\n\t\t\tt.Fatalf(\"want %s but got %s\", testError.message, te.message)\n\t\t}\n\t}\n}\n\nfunc TestOther(t *testing.T) {\n\terr := Errorf(10001, \"test code 10001\", \"message\")\n\t// Code\n\tif !reflect.DeepEqual(Code(nil), 200) {\n\t\tt.Errorf(\"Code(nil) = %v, want %v\", Code(nil), 200)\n\t}\n\tif !reflect.DeepEqual(Code(errors.New(\"test\")), UnknownCode) {\n\t\tt.Errorf(`Code(errors.New(\"test\")) = %v, want %v`, Code(nil), 200)\n\t}\n\tif !reflect.DeepEqual(Code(err), 10001) {\n\t\tt.Errorf(`Code(err) = %v, want %v`, Code(err), 10001)\n\t}\n\t// Reason\n\tif !reflect.DeepEqual(Reason(nil), UnknownReason) {\n\t\tt.Errorf(`Reason(nil) = %v, want %v`, Reason(nil), UnknownReason)\n\t}\n\tif !reflect.DeepEqual(Reason(errors.New(\"test\")), UnknownReason) {\n\t\tt.Errorf(`Reason(errors.New(\"test\")) = %v, want %v`, Reason(nil), UnknownReason)\n\t}\n\tif !reflect.DeepEqual(Reason(err), \"test code 10001\") {\n\t\tt.Errorf(`Reason(err) = %v, want %v`, Reason(err), \"test code 10001\")\n\t}\n\t// Clone\n\terr400 := Newf(http.StatusBadRequest, \"BAD_REQUEST\", \"param invalid\")\n\terr400.Metadata = map[string]string{\n\t\t\"key1\": \"val1\",\n\t\t\"key2\": \"val2\",\n\t}\n\tif cerr := Clone(err400); cerr == nil || cerr.Error() != err400.Error() {\n\t\tt.Errorf(\"Clone(err) = %v, want %v\", Clone(err400), err400)\n\t}\n\tif cerr := Clone(nil); cerr != nil {\n\t\tt.Errorf(\"Clone(nil) = %v, want %v\", Clone(err400), err400)\n\t}\n}\n"
  },
  {
    "path": "errors/types.go",
    "content": "// nolint:mnd\npackage errors\n\n// BadRequest new BadRequest error that is mapped to a 400 response.\nfunc BadRequest(reason, message string) *Error {\n\treturn New(400, reason, message)\n}\n\n// IsBadRequest determines if err is an error which indicates a BadRequest error.\n// It supports wrapped errors.\nfunc IsBadRequest(err error) bool {\n\treturn Code(err) == 400\n}\n\n// Unauthorized new Unauthorized error that is mapped to a 401 response.\nfunc Unauthorized(reason, message string) *Error {\n\treturn New(401, reason, message)\n}\n\n// IsUnauthorized determines if err is an error which indicates an Unauthorized error.\n// It supports wrapped errors.\nfunc IsUnauthorized(err error) bool {\n\treturn Code(err) == 401\n}\n\n// Forbidden new Forbidden error that is mapped to a 403 response.\nfunc Forbidden(reason, message string) *Error {\n\treturn New(403, reason, message)\n}\n\n// IsForbidden determines if err is an error which indicates a Forbidden error.\n// It supports wrapped errors.\nfunc IsForbidden(err error) bool {\n\treturn Code(err) == 403\n}\n\n// NotFound new NotFound error that is mapped to a 404 response.\nfunc NotFound(reason, message string) *Error {\n\treturn New(404, reason, message)\n}\n\n// IsNotFound determines if err is an error which indicates an NotFound error.\n// It supports wrapped errors.\nfunc IsNotFound(err error) bool {\n\treturn Code(err) == 404\n}\n\n// Conflict new Conflict error that is mapped to a 409 response.\nfunc Conflict(reason, message string) *Error {\n\treturn New(409, reason, message)\n}\n\n// IsConflict determines if err is an error which indicates a Conflict error.\n// It supports wrapped errors.\nfunc IsConflict(err error) bool {\n\treturn Code(err) == 409\n}\n\n// TooManyRequests new TooManyRequests error that is mapped to an HTTP 429 response.\nfunc TooManyRequests(reason, message string) *Error {\n\treturn New(429, reason, message)\n}\n\n// IsTooManyRequests determines if err is an error which indicates a TooManyRequests error.\n// It supports wrapped errors.\nfunc IsTooManyRequests(err error) bool {\n\treturn Code(err) == 429\n}\n\n// ClientClosed new ClientClosed error that is mapped to an HTTP 499 response.\nfunc ClientClosed(reason, message string) *Error {\n\treturn New(499, reason, message)\n}\n\n// IsClientClosed determines if err is an error which indicates a IsClientClosed error.\n// It supports wrapped errors.\nfunc IsClientClosed(err error) bool {\n\treturn Code(err) == 499\n}\n\n// InternalServer new InternalServer error that is mapped to a 500 response.\nfunc InternalServer(reason, message string) *Error {\n\treturn New(500, reason, message)\n}\n\n// IsInternalServer determines if err is an error which indicates an Internal error.\n// It supports wrapped errors.\nfunc IsInternalServer(err error) bool {\n\treturn Code(err) == 500\n}\n\n// ServiceUnavailable new ServiceUnavailable error that is mapped to an HTTP 503 response.\nfunc ServiceUnavailable(reason, message string) *Error {\n\treturn New(503, reason, message)\n}\n\n// IsServiceUnavailable determines if err is an error which indicates an Unavailable error.\n// It supports wrapped errors.\nfunc IsServiceUnavailable(err error) bool {\n\treturn Code(err) == 503\n}\n\n// GatewayTimeout new GatewayTimeout error that is mapped to an HTTP 504 response.\nfunc GatewayTimeout(reason, message string) *Error {\n\treturn New(504, reason, message)\n}\n\n// IsGatewayTimeout determines if err is an error which indicates a GatewayTimeout error.\n// It supports wrapped errors.\nfunc IsGatewayTimeout(err error) bool {\n\treturn Code(err) == 504\n}\n"
  },
  {
    "path": "errors/types_test.go",
    "content": "package errors\n\nimport (\n\t\"testing\"\n)\n\nfunc TestTypes(t *testing.T) {\n\tvar (\n\t\tinput = []error{\n\t\t\tBadRequest(\"reason_400\", \"message_400\"),\n\t\t\tUnauthorized(\"reason_401\", \"message_401\"),\n\t\t\tForbidden(\"reason_403\", \"message_403\"),\n\t\t\tNotFound(\"reason_404\", \"message_404\"),\n\t\t\tConflict(\"reason_409\", \"message_409\"),\n\t\t\tInternalServer(\"reason_500\", \"message_500\"),\n\t\t\tServiceUnavailable(\"reason_503\", \"message_503\"),\n\t\t\tGatewayTimeout(\"reason_504\", \"message_504\"),\n\t\t\tClientClosed(\"reason_499\", \"message_499\"),\n\t\t}\n\t\toutput = []func(error) bool{\n\t\t\tIsBadRequest,\n\t\t\tIsUnauthorized,\n\t\t\tIsForbidden,\n\t\t\tIsNotFound,\n\t\t\tIsConflict,\n\t\t\tIsInternalServer,\n\t\t\tIsServiceUnavailable,\n\t\t\tIsGatewayTimeout,\n\t\t\tIsClientClosed,\n\t\t}\n\t)\n\n\tfor i, in := range input {\n\t\tif !output[i](in) {\n\t\t\tt.Errorf(\"not expect: %v\", in)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "errors/wrap.go",
    "content": "package errors\n\nimport (\n\tstderrors \"errors\"\n)\n\n// Is reports whether any error in err's chain matches target.\n//\n// The chain consists of err itself followed by the sequence of errors obtained by\n// repeatedly calling Unwrap.\n//\n// An error is considered to match a target if it is equal to that target or if\n// it implements a method Is(error) bool such that Is(target) returns true.\nfunc Is(err, target error) bool { return stderrors.Is(err, target) }\n\n// As finds the first error in err's chain that matches target, and if so, sets\n// target to that error value and returns true.\n//\n// The chain consists of err itself followed by the sequence of errors obtained by\n// repeatedly calling Unwrap.\n//\n// An error matches target if the error's concrete value is assignable to the value\n// pointed to by target, or if the error has a method As(interface{}) bool such that\n// As(target) returns true. In the latter case, the As method is responsible for\n// setting target.\n//\n// As will panic if target is not a non-nil pointer to either a type that implements\n// error, or to any interface type. As returns false if err is nil.\nfunc As(err error, target any) bool { return stderrors.As(err, target) }\n\n// Unwrap returns the result of calling the Unwrap method on err, if err's\n// type contains an Unwrap method returning error.\n// Otherwise, Unwrap returns nil.\nfunc Unwrap(err error) error {\n\treturn stderrors.Unwrap(err)\n}\n"
  },
  {
    "path": "errors/wrap_test.go",
    "content": "package errors\n\nimport (\n\t\"fmt\"\n\t\"testing\"\n)\n\ntype mockErr struct{}\n\nfunc (*mockErr) Error() string {\n\treturn \"mock error\"\n}\n\nfunc TestWarp(t *testing.T) {\n\tvar err error = &mockErr{}\n\terr2 := fmt.Errorf(\"wrap %w\", err)\n\tif err != Unwrap(err2) {\n\t\tt.Errorf(\"got %v want: %v\", err, Unwrap(err2))\n\t}\n\tif !Is(err2, err) {\n\t\tt.Errorf(\"Is(err2, err) got %v want: %v\", Is(err2, err), true)\n\t}\n\terr3 := &mockErr{}\n\tif !As(err2, &err3) {\n\t\tt.Errorf(\"As(err2, &err3) got %v want: %v\", As(err2, &err3), true)\n\t}\n}\n"
  },
  {
    "path": "go.mod",
    "content": "module github.com/go-kratos/kratos/v2\n\ngo 1.22\n\nrequire (\n\tdario.cat/mergo v1.0.0\n\tgithub.com/fsnotify/fsnotify v1.6.0\n\tgithub.com/go-kratos/aegis v0.2.0\n\tgithub.com/go-playground/form/v4 v4.2.0\n\tgithub.com/golang-jwt/jwt/v5 v5.2.2\n\tgithub.com/google/uuid v1.4.0\n\tgithub.com/gorilla/mux v1.8.1\n\tgo.opentelemetry.io/otel v1.24.0\n\tgo.opentelemetry.io/otel/metric v1.24.0\n\tgo.opentelemetry.io/otel/sdk v1.24.0\n\tgo.opentelemetry.io/otel/sdk/metric v1.24.0\n\tgo.opentelemetry.io/otel/trace v1.24.0\n\tgolang.org/x/sync v0.10.0\n\tgoogle.golang.org/genproto/googleapis/api v0.0.0-20240102182953-50ed04b92917\n\tgoogle.golang.org/genproto/googleapis/rpc v0.0.0-20240102182953-50ed04b92917\n\tgoogle.golang.org/grpc v1.61.1\n\tgoogle.golang.org/protobuf v1.33.0\n\tgopkg.in/yaml.v3 v3.0.1\n)\n\nrequire (\n\tgithub.com/envoyproxy/go-control-plane v0.11.2-0.20230627204322-7d0032219fcb // indirect\n\tgithub.com/go-logr/logr v1.4.1 // indirect\n\tgithub.com/go-logr/stdr v1.2.2 // indirect\n\tgithub.com/go-ole/go-ole v1.2.6 // indirect\n\tgithub.com/golang/protobuf v1.5.4 // indirect\n\tgithub.com/kr/pretty v0.3.1 // indirect\n\tgithub.com/lufia/plan9stats v0.0.0-20230326075908-cb1d2100619a // indirect\n\tgithub.com/power-devops/perfstat v0.0.0-20221212215047-62379fc7944b // indirect\n\tgithub.com/rogpeppe/go-internal v1.11.0 // indirect\n\tgithub.com/shirou/gopsutil/v3 v3.23.6 // indirect\n\tgithub.com/shoenig/go-m1cpu v0.1.6 // indirect\n\tgithub.com/tklauser/go-sysconf v0.3.11 // indirect\n\tgithub.com/tklauser/numcpus v0.6.1 // indirect\n\tgithub.com/yusufpapurcu/wmi v1.2.3 // indirect\n\tgolang.org/x/net v0.33.0 // indirect\n\tgolang.org/x/sys v0.28.0 // indirect\n\tgolang.org/x/text v0.21.0 // indirect\n\tgoogle.golang.org/genproto v0.0.0-20231212172506-995d672761c0 // indirect\n\tgopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect\n)\n\nretract v2.9.0\n"
  },
  {
    "path": "go.sum",
    "content": "dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk=\ndario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=\ngithub.com/census-instrumentation/opencensus-proto v0.4.1 h1:iKLQ0xPNFxR/2hzXZMrBo8f1j86j5WHzznCCQxV/b8g=\ngithub.com/census-instrumentation/opencensus-proto v0.4.1/go.mod h1:4T9NM4+4Vw91VeyqjLS6ao50K5bOcLKN6Q42XnYaRYw=\ngithub.com/cncf/xds/go v0.0.0-20231109132714-523115ebc101 h1:7To3pQ+pZo0i3dsWEbinPNFs5gPSBOsJtx3wTT94VBY=\ngithub.com/cncf/xds/go v0.0.0-20231109132714-523115ebc101/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=\ngithub.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=\ngithub.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.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/envoyproxy/go-control-plane v0.11.2-0.20230627204322-7d0032219fcb h1:kxNVXsNro/lpR5WD+P1FI/yUHn2G03Glber3k8cQL2Y=\ngithub.com/envoyproxy/go-control-plane v0.11.2-0.20230627204322-7d0032219fcb/go.mod h1:GxGqnjWzl1Gz8WfAfMJSfhvsi4EPZayRb25nLHDWXyA=\ngithub.com/envoyproxy/protoc-gen-validate v1.0.2 h1:QkIBuU5k+x7/QXPvPPnWXWlCdaBFApVqftFV6k087DA=\ngithub.com/envoyproxy/protoc-gen-validate v1.0.2/go.mod h1:GpiZQP3dDbg4JouG/NNS7QWXpgx6x8QiMKdmN72jogE=\ngithub.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=\ngithub.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=\ngithub.com/go-kratos/aegis v0.2.0 h1:dObzCDWn3XVjUkgxyBp6ZeWtx/do0DPZ7LY3yNSJLUQ=\ngithub.com/go-kratos/aegis v0.2.0/go.mod h1:v0R2m73WgEEYB3XYu6aE2WcMwsZkJ/Rzuf5eVccm7bI=\ngithub.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=\ngithub.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ=\ngithub.com/go-logr/logr v1.4.1/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-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=\ngithub.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=\ngithub.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A=\ngithub.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=\ngithub.com/go-playground/form/v4 v4.2.0 h1:N1wh+Goz61e6w66vo8vJkQt+uwZSoLz50kZPJWR8eic=\ngithub.com/go-playground/form/v4 v4.2.0/go.mod h1:q1a2BY+AQUUzhl6xA/6hBetay6dEIhMHjgvJiGo6K7U=\ngithub.com/golang-jwt/jwt/v5 v5.2.2 h1:Rl4B7itRWVtYIHFrSNd7vhTiz9UpLdi6gZhZ3wEeDy8=\ngithub.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=\ngithub.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=\ngithub.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=\ngithub.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=\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/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4=\ngithub.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=\ngithub.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=\ngithub.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=\ngithub.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=\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/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I=\ngithub.com/lufia/plan9stats v0.0.0-20230326075908-cb1d2100619a h1:N9zuLhTvBSRt0gWSiJswwQ2HqDmtX/ZCDJURnKUt1Ik=\ngithub.com/lufia/plan9stats v0.0.0-20230326075908-cb1d2100619a/go.mod h1:JKx41uQRwqlTZabZc+kILPrO/3jlKnQ2Z8b7YiVw5cE=\ngithub.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=\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/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=\ngithub.com/power-devops/perfstat v0.0.0-20221212215047-62379fc7944b h1:0LFwY6Q3gMACTjAbMZBjXAqTOzOwFaj2Ld6cjeQ7Rig=\ngithub.com/power-devops/perfstat v0.0.0-20221212215047-62379fc7944b/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=\ngithub.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=\ngithub.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=\ngithub.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=\ngithub.com/shirou/gopsutil/v3 v3.23.6 h1:5y46WPI9QBKBbK7EEccUPNXpJpNrvPuTD0O2zHEHT08=\ngithub.com/shirou/gopsutil/v3 v3.23.6/go.mod h1:j7QX50DrXYggrpN30W0Mo+I4/8U2UUIQrnrhqUeWrAU=\ngithub.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM=\ngithub.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ=\ngithub.com/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU=\ngithub.com/shoenig/test v0.6.4/go.mod h1:byHiCGXqrVaflBLAMq/srcZIHynQPQgeyvkvXnjqq0k=\ngithub.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\ngithub.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=\ngithub.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=\ngithub.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngithub.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=\ngithub.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=\ngithub.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=\ngithub.com/tklauser/go-sysconf v0.3.11 h1:89WgdJhk5SNwJfu+GKyYveZ4IaJ7xAkecBo+KdJV0CM=\ngithub.com/tklauser/go-sysconf v0.3.11/go.mod h1:GqXfhXY3kiPa0nAXPDIQIWzJbMCB7AmcWpGR8lSZfqI=\ngithub.com/tklauser/numcpus v0.6.0/go.mod h1:FEZLMke0lhOUG6w2JadTzp0a+Nl8PF/GFkQ5UVIcaL4=\ngithub.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk=\ngithub.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY=\ngithub.com/yusufpapurcu/wmi v1.2.3 h1:E1ctvB7uKFMOJw3fdOW32DwGE9I7t++CRUEMKvFoFiw=\ngithub.com/yusufpapurcu/wmi v1.2.3/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=\ngo.opentelemetry.io/otel v1.24.0 h1:0LAOdjNmQeSTzGBzduGe/rU4tZhMwL5rWgtp9Ku5Jfo=\ngo.opentelemetry.io/otel v1.24.0/go.mod h1:W7b9Ozg4nkF5tWI5zsXkaKKDjdVjpD4oAt9Qi/MArHo=\ngo.opentelemetry.io/otel/metric v1.24.0 h1:6EhoGWWK28x1fbpA4tYTOWBkPefTDQnb8WSGXlc88kI=\ngo.opentelemetry.io/otel/metric v1.24.0/go.mod h1:VYhLe1rFfxuTXLgj4CBiyz+9WYBA8pNGJgDcSFRKBco=\ngo.opentelemetry.io/otel/sdk v1.24.0 h1:YMPPDNymmQN3ZgczicBY3B6sf9n62Dlj9pWD3ucgoDw=\ngo.opentelemetry.io/otel/sdk v1.24.0/go.mod h1:KVrIYw6tEubO9E96HQpcmpTKDVn9gdv35HoYiQWGDFg=\ngo.opentelemetry.io/otel/sdk/metric v1.24.0 h1:yyMQrPzF+k88/DbH7o4FMAs80puqd+9osbiBrJrz/w8=\ngo.opentelemetry.io/otel/sdk/metric v1.24.0/go.mod h1:I6Y5FjH6rvEnTTAYQz3Mmv2kl6Ek5IIrmwTLqMrrOE0=\ngo.opentelemetry.io/otel/trace v1.24.0 h1:CsKnnL4dUAr/0llH9FKuc698G04IrpWV0MQA/Y1YELI=\ngo.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU=\ngolang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I=\ngolang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=\ngolang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=\ngolang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=\ngolang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=\ngolang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=\ngolang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=\ngolang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=\ngolang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngoogle.golang.org/genproto v0.0.0-20231212172506-995d672761c0 h1:YJ5pD9rF8o9Qtta0Cmy9rdBwkSjrTCT6XTiUQVOtIos=\ngoogle.golang.org/genproto v0.0.0-20231212172506-995d672761c0/go.mod h1:l/k7rMz0vFTBPy+tFSGvXEd3z+BcoG1k7EHbqm+YBsY=\ngoogle.golang.org/genproto/googleapis/api v0.0.0-20240102182953-50ed04b92917 h1:rcS6EyEaoCO52hQDupoSfrxI3R6C2Tq741is7X8OvnM=\ngoogle.golang.org/genproto/googleapis/api v0.0.0-20240102182953-50ed04b92917/go.mod h1:CmlNWB9lSezaYELKS5Ym1r44VrrbPUa7JTvw+6MbpJ0=\ngoogle.golang.org/genproto/googleapis/rpc v0.0.0-20240102182953-50ed04b92917 h1:6G8oQ016D88m1xAKljMlBOOGWDZkes4kMhgGFlf8WcQ=\ngoogle.golang.org/genproto/googleapis/rpc v0.0.0-20240102182953-50ed04b92917/go.mod h1:xtjpI3tXFPP051KaWnhvxkiubL/6dJ18vLVf7q2pTOU=\ngoogle.golang.org/grpc v1.61.1 h1:kLAiWrZs7YeDM6MumDe7m3y4aM6wacLzM1Y/wiLP9XY=\ngoogle.golang.org/grpc v1.61.1/go.mod h1:VUbo7IFqmF1QtCAstipjG0GIoq49KvMe9+h1jFLBNJs=\ngoogle.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=\ngoogle.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=\ngopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/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.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\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": "hack/.lintcheck_failures",
    "content": "\n"
  },
  {
    "path": "hack/.test_ignored_files",
    "content": "./examples\n./cmd/protoc-gen-go-errors\n./cmd/protoc-gen-go-http\n./cmd/kratos\n./contrib/config/kubernetes\n./contrib/registry/kubernetes\n./contrib/registry/zookeeper\n./contrib/registry/eureka\n"
  },
  {
    "path": "hack/resolve-modules.sh",
    "content": "#!/usr/bin/env bash\n\n# This is used by the linter action.\n# Recursively finds all directories with a go.mod file and creates\n# a GitHub Actions JSON output option.\n\nset -o errexit\n\necho \"Resolving modules in $(pwd)\"\n\nKRATOS_HOME=$(\n\tcd \"$(dirname \"${BASH_SOURCE[0]}\")\" &&\n\t\tcd .. &&\n\t\tpwd\n)\n\nsource \"${KRATOS_HOME}/hack/util.sh\"\n\nFAILURE_FILE=${KRATOS_HOME}/hack/.lintcheck_failures\n\nall_modules=$(util::find_modules)\nfailing_modules=()\nwhile IFS='' read -r line; do failing_modules+=(\"$line\"); done < <(cat \"$FAILURE_FILE\")\n\necho \"Ignored failing modules:\"\necho \"${failing_modules[*]}\"\necho\n\nPATHS=\"\"\n\nfor mod in $all_modules; do\n\tutil::array_contains \"$mod\" \"${failing_modules[*]}\" && in_failing=$? || in_failing=$?\n\tif [[ \"$in_failing\" -ne \"0\" ]]; then\n\t\tPATHS+=$(printf '{\"workdir\":\"%s\"},' ${mod})\n\tfi\ndone\n\necho \"::set-output name=matrix::{\\\"include\\\":[${PATHS%?}]}\"\n"
  },
  {
    "path": "hack/tools.sh",
    "content": "#!/usr/bin/env bash\n\n# This is a tools shell script\n# used by Makefile commands\n\nset -o errexit\nset -o nounset\nset -o pipefail\n\nGO111MODULE=on\nKRATOS_HOME=$(\n\tcd \"$(dirname \"${BASH_SOURCE[0]}\")\" &&\n\t\tcd .. &&\n\t\tpwd\n)\n\nsource \"${KRATOS_HOME}/hack/util.sh\"\n\nLINTER=${KRATOS_HOME}/bin/golangci-lint\nLINTER_CONFIG=${KRATOS_HOME}/.golangci.yml\nFAILURE_FILE=${KRATOS_HOME}/hack/.lintcheck_failures\nIGNORED_FILE=${KRATOS_HOME}/hack/.test_ignored_files\n\nall_modules=$(util::find_modules)\nfailing_modules=()\nwhile IFS='' read -r line; do failing_modules+=(\"$line\"); done < <(cat \"$FAILURE_FILE\")\nignored_modules=()\nwhile IFS='' read -r line; do ignored_modules+=(\"$line\"); done < <(cat \"$IGNORED_FILE\")\n\n# functions\n# lint all mod\nfunction lint() {\n\tfor mod in $all_modules; do\n\t\tlocal in_failing\n\t\tutil::array_contains \"$mod\" \"${failing_modules[*]}\" && in_failing=$? || in_failing=$?\n\t\tif [[ \"$in_failing\" -ne \"0\" ]]; then\n\t\t\tpushd \"$mod\" >/dev/null &&\n\t\t\t\techo \"golangci lint $(sed -n 1p go.mod | cut -d ' ' -f2)\" &&\n\t\t\t\teval \"${LINTER} run --timeout=5m --config=${LINTER_CONFIG}\"\n\t\t\tpopd >/dev/null || exit\n\t\tfi\n\tdone\n}\n\n# test all mod\nfunction test() {\n\tfor mod in $all_modules; do\n\t\tlocal in_failing\n\t\tutil::array_contains \"$mod\" \"${ignored_modules[*]}\" && in_failing=$? || in_failing=$?\n\t\tif [[ \"$in_failing\" -ne \"0\" ]]; then\n\t\t\tpushd \"$mod\" >/dev/null &&\n\t\t\t\techo \"go test $(sed -n 1p go.mod | cut -d ' ' -f2)\" &&\n\t\t\t\tgo test -race ./...\n\t\t\tpopd >/dev/null || exit\n\t\tfi\n\tdone\n}\n\nfunction test_coverage() {\n\techo \"\" >coverage.out\n\tlocal base\n\tbase=$(pwd)\n\tfor mod in $all_modules; do\n\t\tlocal in_failing\n\t\tutil::array_contains \"$mod\" \"${ignored_modules[*]}\" && in_failing=$? || in_failing=$?\n\t\tif [[ \"$in_failing\" -ne \"0\" ]]; then\n\t\t\tpushd \"$mod\" >/dev/null &&\n\t\t\t\techo \"go test $(sed -n 1p go.mod | cut -d ' ' -f2)\" &&\n\t\t\t\tgo test -race -coverprofile=profile.out -covermode=atomic ./...\n\t\t\tif [ -f profile.out ]; then\n\t\t\t\tcat profile.out >>\"${base}/coverage.out\"\n\t\t\t\trm profile.out\n\t\t\tfi\n\t\t\tpopd >/dev/null || exit\n\t\tfi\n\tdone\n}\n\n# try to fix all mod with golangci-lint\nfunction fix() {\n\tfor mod in $all_modules; do\n\t\tlocal in_failing\n\t\tutil::array_contains \"$mod\" \"${failing_modules[*]}\" && in_failing=$? || in_failing=$?\n\t\tif [[ \"$in_failing\" -ne \"0\" ]]; then\n\t\t\tpushd \"$mod\" >/dev/null &&\n\t\t\t\techo \"golangci fix $(sed -n 1p go.mod | cut -d ' ' -f2)\" &&\n\t\t\t\teval \"${LINTER} run -v --fix --timeout=5m --config=${LINTER_CONFIG}\"\n\t\t\tpopd >/dev/null || exit\n\t\tfi\n\tdone\n}\n\nfunction tidy() {\n\tfor mod in $all_modules; do\n\t\tpushd \"$mod\" >/dev/null &&\n\t\t\techo \"go mod tidy $(sed -n 1p go.mod | cut -d ' ' -f2)\" &&\n\t\t\tgo mod tidy\n\t\tpopd >/dev/null || exit\n\tdone\n}\n\nfunction help() {\n\techo \"use: lint, test, test_coverage, fix, tidy\"\n}\n\ncase $1 in\nlint)\n\tlint\n\t;;\ntest)\n\ttest\n\t;;\ntest_coverage)\n\ttest_coverage\n\t;;\ntidy)\n\ttidy\n\t;;\nfix)\n\tfix\n\t;;\n*)\n\thelp\n\t;;\nesac\n"
  },
  {
    "path": "hack/util.sh",
    "content": "#!/usr/bin/env bash\n\n# This is a common util functions shell script\n\n# arguments: target, item1, item2, item3, ...\n# returns 0 if target is in the given items, 1 otherwise.\nfunction util::array_contains() {\n\tlocal target=\"$1\"\n\tshift\n\tlocal items=\"$*\"\n\tfor item in ${items[*]}; do\n\t\tif [[ \"${item}\" == \"${target}\" ]]; then\n\t\t\treturn 0\n\t\tfi\n\tdone\n\treturn 1\n}\n\n# find all go mod path\n# returns an array contains mod path\nfunction util::find_modules() {\n\tfind . -not \\( \\\n\t\t\\( \\\n\t\t-path './output' \\\n\t\t-o -path './.git' \\\n\t\t-o -path '*/third_party/*' \\\n\t\t-o -path '*/vendor/*' \\\n\t\t\\) -prune \\\n\t\t\\) -name 'go.mod' -print0 | xargs -0 -I {} dirname {}\n}\n"
  },
  {
    "path": "internal/README.md",
    "content": "# internal\n"
  },
  {
    "path": "internal/context/context.go",
    "content": "package context\n\nimport (\n\t\"context\"\n\t\"sync\"\n\t\"sync/atomic\"\n\t\"time\"\n)\n\ntype mergeCtx struct {\n\tparent1, parent2 context.Context\n\n\tdone     chan struct{}\n\tdoneMark atomic.Bool\n\tdoneOnce sync.Once\n\tdoneErr  error\n\n\tcancelCh   chan struct{}\n\tcancelOnce sync.Once\n}\n\n// Merge merges two contexts into one.\nfunc Merge(parent1, parent2 context.Context) (context.Context, context.CancelFunc) {\n\tmc := &mergeCtx{\n\t\tparent1:  parent1,\n\t\tparent2:  parent2,\n\t\tdone:     make(chan struct{}),\n\t\tcancelCh: make(chan struct{}),\n\t}\n\tselect {\n\tcase <-parent1.Done():\n\t\t_ = mc.finish(parent1.Err())\n\tcase <-parent2.Done():\n\t\t_ = mc.finish(parent2.Err())\n\tdefault:\n\t\tgo mc.wait()\n\t}\n\treturn mc, mc.cancel\n}\n\nfunc (mc *mergeCtx) finish(err error) error {\n\tmc.doneOnce.Do(func() {\n\t\tmc.doneErr = err\n\t\tmc.doneMark.Store(true)\n\t\tclose(mc.done)\n\t})\n\treturn mc.doneErr\n}\n\nfunc (mc *mergeCtx) wait() {\n\tvar err error\n\tselect {\n\tcase <-mc.parent1.Done():\n\t\terr = mc.parent1.Err()\n\tcase <-mc.parent2.Done():\n\t\terr = mc.parent2.Err()\n\tcase <-mc.cancelCh:\n\t\terr = context.Canceled\n\t}\n\t_ = mc.finish(err)\n}\n\nfunc (mc *mergeCtx) cancel() {\n\tmc.cancelOnce.Do(func() {\n\t\tclose(mc.cancelCh)\n\t})\n}\n\n// Done implements context.Context.\nfunc (mc *mergeCtx) Done() <-chan struct{} {\n\treturn mc.done\n}\n\n// Err implements context.Context.\nfunc (mc *mergeCtx) Err() error {\n\tif mc.doneMark.Load() {\n\t\treturn mc.doneErr\n\t}\n\tvar err error\n\tselect {\n\tcase <-mc.parent1.Done():\n\t\terr = mc.parent1.Err()\n\tcase <-mc.parent2.Done():\n\t\terr = mc.parent2.Err()\n\tcase <-mc.cancelCh:\n\t\terr = context.Canceled\n\tdefault:\n\t\treturn nil\n\t}\n\treturn mc.finish(err)\n}\n\n// Deadline implements context.Context.\nfunc (mc *mergeCtx) Deadline() (time.Time, bool) {\n\td1, ok1 := mc.parent1.Deadline()\n\td2, ok2 := mc.parent2.Deadline()\n\tswitch {\n\tcase !ok1:\n\t\treturn d2, ok2\n\tcase !ok2:\n\t\treturn d1, ok1\n\tcase d1.Before(d2):\n\t\treturn d1, true\n\tdefault:\n\t\treturn d2, true\n\t}\n}\n\n// Value implements context.Context.\nfunc (mc *mergeCtx) Value(key any) any {\n\tif v := mc.parent1.Value(key); v != nil {\n\t\treturn v\n\t}\n\treturn mc.parent2.Value(key)\n}\n"
  },
  {
    "path": "internal/context/context_test.go",
    "content": "package context\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"reflect\"\n\t\"testing\"\n\t\"time\"\n)\n\nfunc TestContext(t *testing.T) {\n\ttype ctxKey1 struct{}\n\ttype ctxKey2 struct{}\n\tctx1 := context.WithValue(context.Background(), ctxKey1{}, \"https://github.com/go-kratos/\")\n\tctx2 := context.WithValue(context.Background(), ctxKey2{}, \"https://go-kratos.dev/\")\n\n\tctx, cancel := Merge(ctx1, ctx2)\n\tdefer cancel()\n\n\tgot := ctx.Value(ctxKey1{})\n\tvalue1, ok := got.(string)\n\tif !ok {\n\t\tt.Errorf(\"expect %v, got %v\", true, ok)\n\t}\n\tif !reflect.DeepEqual(value1, \"https://github.com/go-kratos/\") {\n\t\tt.Errorf(\"expect %v, got %v\", \"https://github.com/go-kratos/\", value1)\n\t}\n\n\tgot2 := ctx.Value(ctxKey2{})\n\tvalue2, ok := got2.(string)\n\tif !ok {\n\t\tt.Errorf(\"expect %v, got %v\", true, ok)\n\t}\n\tif !reflect.DeepEqual(\"https://go-kratos.dev/\", value2) {\n\t\tt.Errorf(\"expect %v, got %v\", \"https://go-kratos.dev/\", value2)\n\t}\n\n\tt.Log(value1)\n\tt.Log(value2)\n}\n\nfunc TestMerge(t *testing.T) {\n\ttype ctxKey1 struct{}\n\ttype ctxKey2 struct{}\n\tctx, cancel := context.WithCancel(context.Background())\n\tcancel()\n\tctx1 := context.WithValue(context.Background(), ctxKey1{}, \"https://github.com/go-kratos/\")\n\tctx2 := context.WithValue(ctx, ctxKey2{}, \"https://go-kratos.dev/\")\n\n\tctx, cancel = Merge(ctx1, ctx2)\n\tdefer cancel()\n\n\tgot := ctx.Value(ctxKey1{})\n\tvalue1, ok := got.(string)\n\tif !ok {\n\t\tt.Errorf(\"expect %v, got %v\", true, ok)\n\t}\n\tif !reflect.DeepEqual(value1, \"https://github.com/go-kratos/\") {\n\t\tt.Errorf(\"expect %v, got %v\", \"https://github.com/go-kratos/\", value1)\n\t}\n\n\tgot2 := ctx.Value(ctxKey2{})\n\tvalue2, ok := got2.(string)\n\tif !ok {\n\t\tt.Errorf(\"expect %v, got %v\", true, ok)\n\t}\n\tif !reflect.DeepEqual(value2, \"https://go-kratos.dev/\") {\n\t\tt.Errorf(\"expect %v, got %v\", \" https://go-kratos.dev/\", value2)\n\t}\n\n\tt.Log(ctx)\n}\n\nfunc TestErr(t *testing.T) {\n\tctx1, cancel := context.WithTimeout(context.Background(), time.Microsecond)\n\tdefer cancel()\n\ttime.Sleep(time.Millisecond)\n\n\tctx, cancel := Merge(ctx1, context.Background())\n\tdefer cancel()\n\tif !errors.Is(ctx.Err(), context.DeadlineExceeded) {\n\t\tt.Errorf(\"expect %v, got %v\", context.DeadlineExceeded, ctx.Err())\n\t}\n}\n\nfunc TestDone(t *testing.T) {\n\tctx1, cancel := context.WithCancel(context.Background())\n\tdefer cancel()\n\n\tctx, cancel := Merge(ctx1, context.Background())\n\tgo func() {\n\t\ttime.Sleep(time.Millisecond * 50)\n\t\tcancel()\n\t}()\n\n\tif <-ctx.Done() != struct{}{} {\n\t\tt.Errorf(\"expect %v, got %v\", struct{}{}, <-ctx.Done())\n\t}\n}\n\nfunc TestFinish(t *testing.T) {\n\tmc := &mergeCtx{\n\t\tparent1:  context.Background(),\n\t\tparent2:  context.Background(),\n\t\tdone:     make(chan struct{}),\n\t\tcancelCh: make(chan struct{}),\n\t}\n\terr := mc.finish(context.DeadlineExceeded)\n\tif !errors.Is(err, context.DeadlineExceeded) {\n\t\tt.Errorf(\"expect %v, got %v\", context.DeadlineExceeded, err)\n\t}\n\tif done := mc.doneMark.Load(); done != true {\n\t\tt.Errorf(\"expect %v, got %v\", true, done)\n\t}\n\tif <-mc.done != struct{}{} {\n\t\tt.Errorf(\"expect %v, got %v\", struct{}{}, <-mc.done)\n\t}\n}\n\nfunc TestWait(t *testing.T) {\n\tctx1, cancel := context.WithCancel(context.Background())\n\n\tmc := &mergeCtx{\n\t\tparent1:  ctx1,\n\t\tparent2:  context.Background(),\n\t\tdone:     make(chan struct{}),\n\t\tcancelCh: make(chan struct{}),\n\t}\n\tgo func() {\n\t\ttime.Sleep(time.Millisecond * 50)\n\t\tcancel()\n\t}()\n\n\tmc.wait()\n\tt.Log(mc.doneErr)\n\tif !errors.Is(mc.doneErr, context.Canceled) {\n\t\tt.Errorf(\"expect %v, got %v\", context.Canceled, mc.doneErr)\n\t}\n\n\tctx2, cancel2 := context.WithCancel(context.Background())\n\n\tmc = &mergeCtx{\n\t\tparent1:  ctx2,\n\t\tparent2:  context.Background(),\n\t\tdone:     make(chan struct{}),\n\t\tcancelCh: make(chan struct{}),\n\t}\n\tgo func() {\n\t\ttime.Sleep(time.Millisecond * 50)\n\t\tcancel2()\n\t}()\n\n\tmc.wait()\n\tt.Log(mc.doneErr)\n\tif !errors.Is(mc.doneErr, context.Canceled) {\n\t\tt.Errorf(\"expect %v, got %v\", context.Canceled, mc.doneErr)\n\t}\n}\n\nfunc TestCancel(t *testing.T) {\n\tmc := &mergeCtx{\n\t\tparent1:  context.Background(),\n\t\tparent2:  context.Background(),\n\t\tdone:     make(chan struct{}),\n\t\tcancelCh: make(chan struct{}),\n\t}\n\tmc.cancel()\n\tif <-mc.cancelCh != struct{}{} {\n\t\tt.Errorf(\"expect %v, got %v\", struct{}{}, <-mc.cancelCh)\n\t}\n}\n\nfunc Test_mergeCtx_Deadline(t *testing.T) {\n\ttype fields struct {\n\t\tparent1Timeout time.Time\n\t\tparent2Timeout time.Time\n\t}\n\ttests := []struct {\n\t\tname   string\n\t\tfields fields\n\t\twant1  bool\n\t}{\n\t\t{\n\t\t\tname:   \"parent1 not deadline\",\n\t\t\tfields: fields{time.Time{}, time.Now().Add(time.Second * 100)},\n\t\t\twant1:  true,\n\t\t},\n\t\t{\n\t\t\tname:   \"parent2 not deadline\",\n\t\t\tfields: fields{time.Now().Add(time.Second * 100), time.Time{}},\n\t\t\twant1:  true,\n\t\t},\n\t\t{\n\t\t\tname:   \" parent1 parent2 not deadline\",\n\t\t\tfields: fields{time.Time{}, time.Time{}},\n\t\t\twant1:  false,\n\t\t},\n\t\t{\n\t\t\tname:   \" parent1 < parent2\",\n\t\t\tfields: fields{time.Now().Add(time.Second * 100), time.Now().Add(time.Second * 200)},\n\t\t\twant1:  true,\n\t\t},\n\t\t{\n\t\t\tname:   \" parent1 > parent2\",\n\t\t\tfields: fields{time.Now().Add(time.Second * 100), time.Now().Add(time.Second * 50)},\n\t\t\twant1:  true,\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tvar parent1, parent2 context.Context\n\t\t\tvar cancel1, cancel2 context.CancelFunc\n\t\t\tif reflect.DeepEqual(tt.fields.parent1Timeout, time.Time{}) {\n\t\t\t\tparent1 = context.Background()\n\t\t\t} else {\n\t\t\t\tparent1, cancel1 = context.WithDeadline(context.Background(), tt.fields.parent1Timeout)\n\t\t\t\tdefer cancel1()\n\t\t\t}\n\t\t\tif reflect.DeepEqual(tt.fields.parent2Timeout, time.Time{}) {\n\t\t\t\tparent2 = context.Background()\n\t\t\t} else {\n\t\t\t\tparent2, cancel2 = context.WithDeadline(context.Background(), tt.fields.parent2Timeout)\n\t\t\t\tdefer cancel2()\n\t\t\t}\n\n\t\t\tmc := &mergeCtx{\n\t\t\t\tparent1: parent1,\n\t\t\t\tparent2: parent2,\n\t\t\t}\n\t\t\tgot, got1 := mc.Deadline()\n\t\t\tt.Log(got)\n\t\t\tif got1 != tt.want1 {\n\t\t\t\tt.Errorf(\"Deadline() got1 = %v, want %v\", got1, tt.want1)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc Test_Err2(t *testing.T) {\n\tctx1, cancel := context.WithCancel(context.Background())\n\tdefer cancel()\n\ttime.Sleep(time.Millisecond)\n\n\tctx, cancel := Merge(ctx1, context.Background())\n\tdefer cancel()\n\n\tif ctx.Err() != nil {\n\t\tt.Errorf(\"expect %v, got %v\", nil, ctx.Err())\n\t}\n\n\tctx1, cancel1 := context.WithCancel(context.Background())\n\ttime.Sleep(time.Millisecond)\n\n\tctx, cancel = Merge(ctx1, context.Background())\n\tdefer cancel()\n\n\tcancel1()\n\n\tif !errors.Is(ctx.Err(), context.Canceled) {\n\t\tt.Errorf(\"expect %v, got %v\", context.Canceled, ctx.Err())\n\t}\n\n\tctx1, cancel1 = context.WithCancel(context.Background())\n\ttime.Sleep(time.Millisecond)\n\n\tctx, cancel = Merge(context.Background(), ctx1)\n\tdefer cancel()\n\n\tcancel1()\n\n\tif !errors.Is(ctx.Err(), context.Canceled) {\n\t\tt.Errorf(\"expect %v, got %v\", context.Canceled, ctx.Err())\n\t}\n\n\tctx, cancel = Merge(context.Background(), context.Background())\n\tcancel()\n\tif !errors.Is(ctx.Err(), context.Canceled) {\n\t\tt.Errorf(\"expect %v, got %v\", context.Canceled, ctx.Err())\n\t}\n}\n"
  },
  {
    "path": "internal/endpoint/endpoint.go",
    "content": "package endpoint\n\nimport (\n\t\"net/url\"\n)\n\n// NewEndpoint new an Endpoint URL.\nfunc NewEndpoint(scheme, host string) *url.URL {\n\treturn &url.URL{Scheme: scheme, Host: host}\n}\n\n// ParseEndpoint parses an Endpoint URL.\nfunc ParseEndpoint(endpoints []string, scheme string) (string, error) {\n\tfor _, e := range endpoints {\n\t\tu, err := url.Parse(e)\n\t\tif err != nil {\n\t\t\treturn \"\", err\n\t\t}\n\n\t\tif u.Scheme == scheme {\n\t\t\treturn u.Host, nil\n\t\t}\n\t}\n\treturn \"\", nil\n}\n\n// Scheme is the scheme of endpoint url.\n// examples: scheme=\"http\",isSecure=true get \"https\"\nfunc Scheme(scheme string, isSecure bool) string {\n\tif isSecure {\n\t\treturn scheme + \"s\"\n\t}\n\treturn scheme\n}\n"
  },
  {
    "path": "internal/endpoint/endpoint_test.go",
    "content": "package endpoint\n\nimport (\n\t\"net/url\"\n\t\"reflect\"\n\t\"testing\"\n)\n\nfunc TestNewEndpoint(t *testing.T) {\n\ttype args struct {\n\t\tscheme string\n\t\thost   string\n\t}\n\ttests := []struct {\n\t\tname string\n\t\targs args\n\t\twant *url.URL\n\t}{\n\t\t{\n\t\t\tname: \"https://github.com/go-kratos/kratos/\",\n\t\t\targs: args{\"https\", \"github.com/go-kratos/kratos/\"},\n\t\t\twant: &url.URL{Scheme: \"https\", Host: \"github.com/go-kratos/kratos/\"},\n\t\t},\n\t\t{\n\t\t\tname: \"https://go-kratos.dev/\",\n\t\t\targs: args{\"https\", \"go-kratos.dev/\"},\n\t\t\twant: &url.URL{Scheme: \"https\", Host: \"go-kratos.dev/\"},\n\t\t},\n\t\t{\n\t\t\tname: \"https://www.google.com/\",\n\t\t\targs: args{\"https\", \"www.google.com/\"},\n\t\t\twant: &url.URL{Scheme: \"https\", Host: \"www.google.com/\"},\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tif got := NewEndpoint(tt.args.scheme, tt.args.host); !reflect.DeepEqual(got, tt.want) {\n\t\t\t\tt.Errorf(\"NewEndpoint() = %v, want %v\", got, tt.want)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestParseEndpoint(t *testing.T) {\n\ttype args struct {\n\t\tendpoints []string\n\t\tscheme    string\n\t}\n\ttests := []struct {\n\t\tname    string\n\t\targs    args\n\t\twant    string\n\t\twantErr bool\n\t}{\n\t\t{\n\t\t\tname:    \"kratos\",\n\t\t\targs:    args{endpoints: []string{\"https://github.com/go-kratos/kratos\"}, scheme: \"https\"},\n\t\t\twant:    \"github.com\",\n\t\t\twantErr: false,\n\t\t},\n\t\t{\n\t\t\tname:    \"test\",\n\t\t\targs:    args{endpoints: []string{\"http://go-kratos.dev/\"}, scheme: \"https\"},\n\t\t\twant:    \"\",\n\t\t\twantErr: false,\n\t\t},\n\t\t{\n\t\t\tname:    \"localhost:8080\",\n\t\t\targs:    args{endpoints: []string{\"grpcs://localhost:8080/\"}, scheme: \"grpcs\"},\n\t\t\twant:    \"localhost:8080\",\n\t\t\twantErr: false,\n\t\t},\n\t\t{\n\t\t\tname:    \"localhost:8081\",\n\t\t\targs:    args{endpoints: []string{\"grpcs://localhost:8080/\"}, scheme: \"grpc\"},\n\t\t\twant:    \"\",\n\t\t\twantErr: false,\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tgot, err := ParseEndpoint(tt.args.endpoints, tt.args.scheme)\n\t\t\tif (err != nil) != tt.wantErr {\n\t\t\t\tt.Errorf(\"ParseEndpoint() error = %v, wantErr %v\", err, tt.wantErr)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif got != tt.want {\n\t\t\t\tt.Errorf(\"ParseEndpoint() got = %v, want %v\", got, tt.want)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestSchema(t *testing.T) {\n\ttests := []struct {\n\t\tschema string\n\t\tsecure bool\n\t\twant   string\n\t}{\n\t\t{\n\t\t\tschema: \"http\",\n\t\t\tsecure: true,\n\t\t\twant:   \"https\",\n\t\t},\n\t\t{\n\t\t\tschema: \"http\",\n\t\t\tsecure: false,\n\t\t\twant:   \"http\",\n\t\t},\n\t\t{\n\t\t\tschema: \"grpc\",\n\t\t\tsecure: true,\n\t\t\twant:   \"grpcs\",\n\t\t},\n\t\t{\n\t\t\tschema: \"grpc\",\n\t\t\tsecure: false,\n\t\t\twant:   \"grpc\",\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tif got := Scheme(tt.schema, tt.secure); got != tt.want {\n\t\t\tt.Errorf(\"Schema() = %v, want %v\", got, tt.want)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "internal/group/example_test.go",
    "content": "package group\n\nimport \"fmt\"\n\ntype Counter struct {\n\tValue int\n}\n\nfunc (c *Counter) Incr() {\n\tc.Value++\n}\n\nfunc ExampleGroup_Get() {\n\tgroup := NewGroup(func() any {\n\t\tfmt.Println(\"Only Once\")\n\t\treturn &Counter{}\n\t})\n\n\t// Create a new Counter\n\tgroup.Get(\"pass\").(*Counter).Incr()\n\n\t// Get the created Counter again.\n\tgroup.Get(\"pass\").(*Counter).Incr()\n\t// Output:\n\t// Only Once\n}\n\nfunc ExampleGroup_Reset() {\n\tgroup := NewGroup(func() any {\n\t\treturn &Counter{}\n\t})\n\n\t// Reset the new function and clear all created objects.\n\tgroup.Reset(func() any {\n\t\tfmt.Println(\"reset\")\n\t\treturn &Counter{}\n\t})\n\n\t// Create a new Counter\n\tgroup.Get(\"pass\").(*Counter).Incr()\n\t// Output:reset\n}\n"
  },
  {
    "path": "internal/group/group.go",
    "content": "// Package group provides a sample lazy load container.\n// The group only creating a new object not until the object is needed by user.\n// And it will cache all the objects to reduce the creation of object.\npackage group\n\nimport \"sync\"\n\n// Factory is a function that creates an object of type T.\ntype Factory[T any] func() T\n\n// Group is a lazy load container.\ntype Group[T any] struct {\n\tfactory func() T\n\tvals    map[string]T\n\tsync.RWMutex\n}\n\n// NewGroup news a group container.\nfunc NewGroup[T any](factory Factory[T]) *Group[T] {\n\tif factory == nil {\n\t\tpanic(\"container.group: can't assign a nil to the new function\")\n\t}\n\treturn &Group[T]{\n\t\tfactory: factory,\n\t\tvals:    make(map[string]T),\n\t}\n}\n\n// Get gets the object by the given key.\nfunc (g *Group[T]) Get(key string) T {\n\tg.RLock()\n\tv, ok := g.vals[key]\n\tif ok {\n\t\tg.RUnlock()\n\t\treturn v\n\t}\n\tg.RUnlock()\n\n\t// slow path for group don`t have specified key value\n\tg.Lock()\n\tdefer g.Unlock()\n\tv, ok = g.vals[key]\n\tif ok {\n\t\treturn v\n\t}\n\tv = g.factory()\n\tg.vals[key] = v\n\treturn v\n}\n\n// Reset resets the new function and deletes all existing objects.\nfunc (g *Group[T]) Reset(factory Factory[T]) {\n\tif factory == nil {\n\t\tpanic(\"container.group: can't assign a nil to the new function\")\n\t}\n\tg.Lock()\n\tg.factory = factory\n\tg.Unlock()\n\tg.Clear()\n}\n\n// Clear deletes all objects.\nfunc (g *Group[T]) Clear() {\n\tg.Lock()\n\tg.vals = make(map[string]T)\n\tg.Unlock()\n}\n"
  },
  {
    "path": "internal/group/group_test.go",
    "content": "package group\n\nimport (\n\t\"reflect\"\n\t\"testing\"\n)\n\nfunc TestGroupGet(t *testing.T) {\n\tcount := 0\n\tg := NewGroup[int](func() int {\n\t\tcount++\n\t\treturn count\n\t})\n\tv := g.Get(\"key_0\")\n\tif !reflect.DeepEqual(v, 1) {\n\t\tt.Errorf(\"expect 1, actual %v\", v)\n\t}\n\n\tv = g.Get(\"key_1\")\n\tif !reflect.DeepEqual(v, 2) {\n\t\tt.Errorf(\"expect 2, actual %v\", v)\n\t}\n\n\tv = g.Get(\"key_0\")\n\tif !reflect.DeepEqual(v, 1) {\n\t\tt.Errorf(\"expect 1, actual %v\", v)\n\t}\n\tif !reflect.DeepEqual(count, 2) {\n\t\tt.Errorf(\"expect count 2, actual %v\", count)\n\t}\n}\n\nfunc TestGroupReset(t *testing.T) {\n\tg := NewGroup(func() int {\n\t\treturn 1\n\t})\n\tg.Get(\"key\")\n\tcall := false\n\tg.Reset(func() int {\n\t\tcall = true\n\t\treturn 1\n\t})\n\n\tlength := 0\n\tfor range g.vals {\n\t\tlength++\n\t}\n\tif !reflect.DeepEqual(length, 0) {\n\t\tt.Errorf(\"expect length 0, actual %v\", length)\n\t}\n\n\tg.Get(\"key\")\n\tif !reflect.DeepEqual(call, true) {\n\t\tt.Errorf(\"expect call true, actual %v\", call)\n\t}\n}\n\nfunc TestGroupClear(t *testing.T) {\n\tg := NewGroup(func() int {\n\t\treturn 1\n\t})\n\tg.Get(\"key\")\n\tlength := 0\n\tfor range g.vals {\n\t\tlength++\n\t}\n\tif !reflect.DeepEqual(length, 1) {\n\t\tt.Errorf(\"expect length 1, actual %v\", length)\n\t}\n\n\tg.Clear()\n\tlength = 0\n\tfor range g.vals {\n\t\tlength++\n\t}\n\tif !reflect.DeepEqual(length, 0) {\n\t\tt.Errorf(\"expect length 0, actual %v\", length)\n\t}\n}\n"
  },
  {
    "path": "internal/host/host.go",
    "content": "package host\n\nimport (\n\t\"fmt\"\n\t\"net\"\n\t\"strconv\"\n)\n\n// ExtractHostPort from address\nfunc ExtractHostPort(addr string) (host string, port uint64, err error) {\n\tvar ports string\n\thost, ports, err = net.SplitHostPort(addr)\n\tif err != nil {\n\t\treturn\n\t}\n\tport, err = strconv.ParseUint(ports, 10, 16) //nolint:mnd\n\treturn\n}\n\nfunc isValidIP(addr string) bool {\n\tip := net.ParseIP(addr)\n\treturn ip.IsGlobalUnicast() && !ip.IsInterfaceLocalMulticast()\n}\n\n// Port return a real port.\nfunc Port(lis net.Listener) (int, bool) {\n\tif addr, ok := lis.Addr().(*net.TCPAddr); ok {\n\t\treturn addr.Port, true\n\t}\n\treturn 0, false\n}\n\n// Extract returns a private addr and port.\nfunc Extract(hostPort string, lis net.Listener) (string, error) {\n\taddr, port, err := net.SplitHostPort(hostPort)\n\tif err != nil && lis == nil {\n\t\treturn \"\", err\n\t}\n\tif lis != nil {\n\t\tp, ok := Port(lis)\n\t\tif !ok {\n\t\t\treturn \"\", fmt.Errorf(\"failed to extract port: %v\", lis.Addr())\n\t\t}\n\t\tport = strconv.Itoa(p)\n\t}\n\tif len(addr) > 0 && (addr != \"0.0.0.0\" && addr != \"[::]\" && addr != \"::\") {\n\t\treturn net.JoinHostPort(addr, port), nil\n\t}\n\tifaces, err := net.Interfaces()\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\tvar (\n\t\tminIndex = 0\n\t\tips      = make([]net.IP, 0, 1)\n\t)\n\tfor _, iface := range ifaces {\n\t\tif iface.Flags&net.FlagUp == 0 {\n\t\t\tcontinue\n\t\t}\n\t\tif iface.Index >= minIndex && len(ips) != 0 {\n\t\t\tcontinue\n\t\t}\n\t\taddrs, err := iface.Addrs()\n\t\tif err != nil {\n\t\t\tcontinue\n\t\t}\n\t\tfor _, rawAddr := range addrs {\n\t\t\tvar ip net.IP\n\t\t\tswitch addr := rawAddr.(type) {\n\t\t\tcase *net.IPAddr:\n\t\t\t\tip = addr.IP\n\t\t\tcase *net.IPNet:\n\t\t\t\tip = addr.IP\n\t\t\tdefault:\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif isValidIP(ip.String()) {\n\t\t\t\tminIndex = iface.Index\n\t\t\t\tips = append(ips, ip)\n\t\t\t\tif ip.To4() != nil {\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\tif len(ips) != 0 {\n\t\treturn net.JoinHostPort(ips[len(ips)-1].String(), port), nil\n\t}\n\treturn \"\", nil\n}\n"
  },
  {
    "path": "internal/host/host_test.go",
    "content": "package host\n\nimport (\n\t\"net\"\n\t\"reflect\"\n\t\"testing\"\n)\n\nfunc TestValidIP(t *testing.T) {\n\ttests := []struct {\n\t\taddr   string\n\t\texpect bool\n\t}{\n\t\t{\"127.0.0.1\", false},\n\t\t{\"255.255.255.255\", false},\n\t\t{\"0.0.0.0\", false},\n\t\t{\"localhost\", false},\n\t\t{\"10.1.0.1\", true},\n\t\t{\"172.16.0.1\", true},\n\t\t{\"192.168.1.1\", true},\n\t\t{\"8.8.8.8\", true},\n\t\t{\"1.1.1.1\", true},\n\t\t{\"9.255.255.255\", true},\n\t\t{\"10.0.0.0\", true},\n\t\t{\"10.255.255.255\", true},\n\t\t{\"11.0.0.0\", true},\n\t\t{\"172.15.255.255\", true},\n\t\t{\"172.16.0.0\", true},\n\t\t{\"172.16.255.255\", true},\n\t\t{\"172.23.18.255\", true},\n\t\t{\"172.31.255.255\", true},\n\t\t{\"172.31.0.0\", true},\n\t\t{\"172.32.0.0\", true},\n\t\t{\"192.167.255.255\", true},\n\t\t{\"192.168.0.0\", true},\n\t\t{\"192.168.255.255\", true},\n\t\t{\"192.169.0.0\", true},\n\t\t{\"fbff:ffff:ffff:ffff:ffff:ffff:ffff:ffff\", true},\n\t\t{\"fc00::\", true},\n\t\t{\"fcff:1200:0:44::\", true},\n\t\t{\"fdff:ffff:ffff:ffff:ffff:ffff:ffff:ffff\", true},\n\t\t{\"fe00::\", true},\n\t}\n\tfor _, test := range tests {\n\t\tt.Run(test.addr, func(t *testing.T) {\n\t\t\tres := isValidIP(test.addr)\n\t\t\tif res != test.expect {\n\t\t\t\tt.Fatalf(\"expected %t got %t\", test.expect, res)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestExtract(t *testing.T) {\n\ttests := []struct {\n\t\taddr   string\n\t\texpect string\n\t}{\n\t\t{\"127.0.0.1:80\", \"127.0.0.1:80\"},\n\t\t{\"10.0.0.1:80\", \"10.0.0.1:80\"},\n\t\t{\"172.16.0.1:80\", \"172.16.0.1:80\"},\n\t\t{\"192.168.1.1:80\", \"192.168.1.1:80\"},\n\t\t{\"0.0.0.0:80\", \"\"},\n\t\t{\"[::]:80\", \"\"},\n\t\t{\":80\", \"\"},\n\t}\n\tfor _, test := range tests {\n\t\tt.Run(test.addr, func(t *testing.T) {\n\t\t\tres, err := Extract(test.addr, nil)\n\t\t\tif err != nil {\n\t\t\t\tt.Fatal(err)\n\t\t\t}\n\t\t\tif res != test.expect && (test.expect == \"\" && test.addr == test.expect) {\n\t\t\t\tt.Fatalf(\"expected %s got %s\", test.expect, res)\n\t\t\t}\n\t\t})\n\t}\n\tlis, err := net.Listen(\"tcp\", \":12345\")\n\tif err != nil {\n\t\tt.Errorf(\"expected: %v got %v\", nil, err)\n\t}\n\tres, err := Extract(\"\", lis)\n\tif err != nil {\n\t\tt.Errorf(\"expected: %v got %v\", nil, err)\n\t}\n\texpect, err := Extract(lis.Addr().String(), nil)\n\tif err != nil {\n\t\tt.Errorf(\"expected: %v got %v\", nil, err)\n\t}\n\tif !reflect.DeepEqual(res, expect) {\n\t\tt.Errorf(\"expected %s got %s\", expect, res)\n\t}\n}\n\nfunc TestExtract2(t *testing.T) {\n\taddr := \"localhost:9001\"\n\tlis, err := net.Listen(\"tcp\", addr)\n\tif err != nil {\n\t\tt.Errorf(\"expected: %v got %v\", nil, err)\n\t}\n\tres, err := Extract(addr, lis)\n\tif err != nil {\n\t\tt.Errorf(\"expected: %v got %v\", nil, err)\n\t}\n\tif !reflect.DeepEqual(res, \"localhost:9001\") {\n\t\tt.Errorf(\"expected %s got %s\", \"localhost:9001\", res)\n\t}\n}\n\nfunc TestPort(t *testing.T) {\n\tlis, err := net.Listen(\"tcp\", \":0\")\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tport, ok := Port(lis)\n\tif !ok || port == 0 {\n\t\tt.Fatalf(\"expected: %s got %d\", lis.Addr().String(), port)\n\t}\n}\n\nfunc TestExtractHostPort(t *testing.T) {\n\thost, port, err := ExtractHostPort(\"127.0.0.1:8000\")\n\tif err != nil {\n\t\tt.Fatalf(\"expected: %v got %v\", nil, err)\n\t}\n\tt.Logf(\"host port: %s,  %d\", host, port)\n\n\thost, port, err = ExtractHostPort(\"www.bilibili.com:80\")\n\tif err != nil {\n\t\tt.Fatalf(\"expected: %v got %v\", nil, err)\n\t}\n\tt.Logf(\"host port: %s,  %d\", host, port)\n\n\thost, port, err = ExtractHostPort(\"consul://2/33\")\n\tif err == nil {\n\t\tt.Fatalf(\"expected: not nil got %v\", nil)\n\t}\n\tt.Logf(\"host port: %s,  %d\", host, port)\n}\n\nfunc TestIpIsUp(t *testing.T) {\n\tinterfaces, err := net.Interfaces()\n\tif err != nil {\n\t\tt.Fail()\n\t}\n\tfor i := range interfaces {\n\t\tprintln(interfaces[i].Name, interfaces[i].Flags&net.FlagUp)\n\t}\n}\n"
  },
  {
    "path": "internal/httputil/http.go",
    "content": "package httputil\n\nimport (\n\t\"strings\"\n)\n\nconst (\n\tbaseContentType = \"application\"\n)\n\n// ContentType returns the content-type with base prefix.\nfunc ContentType(subtype string) string {\n\treturn baseContentType + \"/\" + subtype\n}\n\n// ContentSubtype returns the content-subtype for the given content-type. The\n// given content-type must be a valid content-type that starts with\n// but no content-subtype will be returned.\n// according rfc7231.\n// contentType is assumed to be lowercase already.\nfunc ContentSubtype(contentType string) string {\n\tleft := strings.Index(contentType, \"/\")\n\tif left == -1 {\n\t\treturn \"\"\n\t}\n\tright := strings.Index(contentType, \";\")\n\tif right == -1 {\n\t\tright = len(contentType)\n\t}\n\tif right < left {\n\t\treturn \"\"\n\t}\n\treturn contentType[left+1 : right]\n}\n"
  },
  {
    "path": "internal/httputil/http_test.go",
    "content": "package httputil\n\nimport (\n\t\"testing\"\n)\n\nfunc TestContentSubtype(t *testing.T) {\n\ttests := []struct {\n\t\tcontentType string\n\t\twant        string\n\t}{\n\t\t{\"text/html; charset=utf-8\", \"html\"},\n\t\t{\"multipart/form-data; boundary=something\", \"form-data\"},\n\t\t{\"application/json; charset=utf-8\", \"json\"},\n\t\t{\"application/json\", \"json\"},\n\t\t{\"application/xml\", \"xml\"},\n\t\t{\"text/xml\", \"xml\"},\n\t\t{\";text/xml\", \"\"},\n\t\t{\"application\", \"\"},\n\t}\n\tfor _, test := range tests {\n\t\tt.Run(test.contentType, func(t *testing.T) {\n\t\t\tgot := ContentSubtype(test.contentType)\n\t\t\tif got != test.want {\n\t\t\t\tt.Fatalf(\"want %v got %v\", test.want, got)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestContentType(t *testing.T) {\n\ttests := []struct {\n\t\tname    string\n\t\tsubtype string\n\t\twant    string\n\t}{\n\t\t{\"kratos\", \"kratos\", \"application/kratos\"},\n\t\t{\"json\", \"json\", \"application/json\"},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tif got := ContentType(tt.subtype); got != tt.want {\n\t\t\t\tt.Errorf(\"ContentType() = %v, want %v\", got, tt.want)\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "internal/matcher/middleware.go",
    "content": "package matcher\n\nimport (\n\t\"sort\"\n\t\"strings\"\n\n\t\"github.com/go-kratos/kratos/v2/middleware\"\n)\n\n// Matcher is a middleware matcher.\ntype Matcher interface {\n\tUse(ms ...middleware.Middleware)\n\tAdd(selector string, ms ...middleware.Middleware)\n\tMatch(operation string) []middleware.Middleware\n}\n\n// New new a middleware matcher.\nfunc New() Matcher {\n\treturn &matcher{\n\t\tmatches: make(map[string][]middleware.Middleware),\n\t}\n}\n\ntype matcher struct {\n\tprefix   []string\n\tdefaults []middleware.Middleware\n\tmatches  map[string][]middleware.Middleware\n}\n\nfunc (m *matcher) Use(ms ...middleware.Middleware) {\n\tm.defaults = ms\n}\n\nfunc (m *matcher) Add(selector string, ms ...middleware.Middleware) {\n\tif strings.HasSuffix(selector, \"*\") {\n\t\tselector = strings.TrimSuffix(selector, \"*\")\n\t\tm.prefix = append(m.prefix, selector)\n\t\t// sort the prefix:\n\t\t//  - /foo/bar\n\t\t//  - /foo\n\t\tsort.Slice(m.prefix, func(i, j int) bool {\n\t\t\treturn m.prefix[i] > m.prefix[j]\n\t\t})\n\t}\n\tm.matches[selector] = ms\n}\n\nfunc (m *matcher) Match(operation string) []middleware.Middleware {\n\tms := make([]middleware.Middleware, 0, len(m.defaults))\n\tif len(m.defaults) > 0 {\n\t\tms = append(ms, m.defaults...)\n\t}\n\tif next, ok := m.matches[operation]; ok {\n\t\treturn append(ms, next...)\n\t}\n\tfor _, prefix := range m.prefix {\n\t\tif strings.HasPrefix(operation, prefix) {\n\t\t\treturn append(ms, m.matches[prefix]...)\n\t\t}\n\t}\n\treturn ms\n}\n"
  },
  {
    "path": "internal/matcher/middleware_test.go",
    "content": "package matcher\n\nimport (\n\t\"context\"\n\t\"testing\"\n\n\t\"github.com/go-kratos/kratos/v2/middleware\"\n)\n\nfunc logging(module string) middleware.Middleware {\n\treturn func(middleware.Handler) middleware.Handler {\n\t\treturn func(context.Context, any) (reply any, err error) {\n\t\t\treturn module, nil\n\t\t}\n\t}\n}\n\nfunc equal(ms []middleware.Middleware, modules ...string) bool {\n\tif len(ms) == 0 {\n\t\treturn false\n\t}\n\tfor i, m := range ms {\n\t\tx, _ := m(nil)(nil, nil)\n\t\tif x != modules[i] {\n\t\t\treturn false\n\t\t}\n\t}\n\treturn true\n}\n\nfunc TestMatcher(t *testing.T) {\n\tm := New()\n\tm.Use(logging(\"logging\"))\n\tm.Add(\"*\", logging(\"*\"))\n\tm.Add(\"/foo/*\", logging(\"foo/*\"))\n\tm.Add(\"/foo/bar/*\", logging(\"foo/bar/*\"))\n\tm.Add(\"/foo/bar\", logging(\"foo/bar\"))\n\n\tif ms := m.Match(\"/\"); len(ms) != 2 {\n\t\tt.Fatal(\"not equal\")\n\t} else if !equal(ms, \"logging\", \"*\") {\n\t\tt.Fatal(\"not equal\")\n\t}\n\n\tif ms := m.Match(\"/foo/xxx\"); len(ms) != 2 {\n\t\tt.Fatal(\"not equal\")\n\t} else if !equal(ms, \"logging\", \"foo/*\") {\n\t\tt.Fatal(\"not equal\")\n\t}\n\n\tif ms := m.Match(\"/foo/bar\"); len(ms) != 2 {\n\t\tt.Fatal(\"not equal\")\n\t} else if !equal(ms, \"logging\", \"foo/bar\") {\n\t\tt.Fatal(\"not equal\")\n\t}\n\n\tif ms := m.Match(\"/foo/bar/x\"); len(ms) != 2 {\n\t\tt.Fatal(\"not equal\")\n\t} else if !equal(ms, \"logging\", \"foo/bar/*\") {\n\t\tt.Fatal(\"not equal\")\n\t}\n}\n"
  },
  {
    "path": "internal/testdata/binding/generate.go",
    "content": "package binding\n\n//go:generate protoc -I . --go_out=paths=source_relative:. ./test.proto\n"
  },
  {
    "path": "internal/testdata/binding/test.pb.go",
    "content": "// Code generated by protoc-gen-go. DO NOT EDIT.\n// versions:\n// \tprotoc-gen-go v1.28.0\n// \tprotoc        v3.17.3\n// source: test.proto\n\npackage binding\n\nimport (\n\tprotoreflect \"google.golang.org/protobuf/reflect/protoreflect\"\n\tprotoimpl \"google.golang.org/protobuf/runtime/protoimpl\"\n\tfieldmaskpb \"google.golang.org/protobuf/types/known/fieldmaskpb\"\n\treflect \"reflect\"\n\tsync \"sync\"\n)\n\nconst (\n\t// Verify that this generated code is sufficiently up-to-date.\n\t_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)\n\t// Verify that runtime/protoimpl is sufficiently up-to-date.\n\t_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)\n)\n\n// The request message containing the user's name.\ntype HelloRequest struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\tName         string                 `protobuf:\"bytes,1,opt,name=name,proto3\" json:\"name,omitempty\"`\n\tSub          *Sub                   `protobuf:\"bytes,2,opt,name=sub,proto3\" json:\"sub,omitempty\"`\n\tUpdateMask   *fieldmaskpb.FieldMask `protobuf:\"bytes,3,opt,name=update_mask,json=updateMask,proto3\" json:\"update_mask,omitempty\"`\n\tOptInt32     *int32                 `protobuf:\"varint,4,opt,name=opt_int32,json=optInt32,proto3,oneof\" json:\"opt_int32,omitempty\"`\n\tOptInt64     *int64                 `protobuf:\"varint,5,opt,name=opt_int64,json=optInt64,proto3,oneof\" json:\"opt_int64,omitempty\"`\n\tOptString    *string                `protobuf:\"bytes,6,opt,name=opt_string,json=optString,proto3,oneof\" json:\"opt_string,omitempty\"`\n\tSubField     *Sub                   `protobuf:\"bytes,7,opt,name=subField,proto3\" json:\"subField,omitempty\"`\n\tTestRepeated []string               `protobuf:\"bytes,8,rep,name=test_repeated,proto3\" json:\"test_repeated,omitempty\"`\n}\n\nfunc (x *HelloRequest) Reset() {\n\t*x = HelloRequest{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_test_proto_msgTypes[0]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *HelloRequest) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*HelloRequest) ProtoMessage() {}\n\nfunc (x *HelloRequest) ProtoReflect() protoreflect.Message {\n\tmi := &file_test_proto_msgTypes[0]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use HelloRequest.ProtoReflect.Descriptor instead.\nfunc (*HelloRequest) Descriptor() ([]byte, []int) {\n\treturn file_test_proto_rawDescGZIP(), []int{0}\n}\n\nfunc (x *HelloRequest) GetName() string {\n\tif x != nil {\n\t\treturn x.Name\n\t}\n\treturn \"\"\n}\n\nfunc (x *HelloRequest) GetSub() *Sub {\n\tif x != nil {\n\t\treturn x.Sub\n\t}\n\treturn nil\n}\n\nfunc (x *HelloRequest) GetUpdateMask() *fieldmaskpb.FieldMask {\n\tif x != nil {\n\t\treturn x.UpdateMask\n\t}\n\treturn nil\n}\n\nfunc (x *HelloRequest) GetOptInt32() int32 {\n\tif x != nil && x.OptInt32 != nil {\n\t\treturn *x.OptInt32\n\t}\n\treturn 0\n}\n\nfunc (x *HelloRequest) GetOptInt64() int64 {\n\tif x != nil && x.OptInt64 != nil {\n\t\treturn *x.OptInt64\n\t}\n\treturn 0\n}\n\nfunc (x *HelloRequest) GetOptString() string {\n\tif x != nil && x.OptString != nil {\n\t\treturn *x.OptString\n\t}\n\treturn \"\"\n}\n\nfunc (x *HelloRequest) GetSubField() *Sub {\n\tif x != nil {\n\t\treturn x.SubField\n\t}\n\treturn nil\n}\n\nfunc (x *HelloRequest) GetTestRepeated() []string {\n\tif x != nil {\n\t\treturn x.TestRepeated\n\t}\n\treturn nil\n}\n\ntype Sub struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\tName string `protobuf:\"bytes,1,opt,name=name,json=naming,proto3\" json:\"name,omitempty\"`\n}\n\nfunc (x *Sub) Reset() {\n\t*x = Sub{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_test_proto_msgTypes[1]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *Sub) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*Sub) ProtoMessage() {}\n\nfunc (x *Sub) ProtoReflect() protoreflect.Message {\n\tmi := &file_test_proto_msgTypes[1]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use Sub.ProtoReflect.Descriptor instead.\nfunc (*Sub) Descriptor() ([]byte, []int) {\n\treturn file_test_proto_rawDescGZIP(), []int{1}\n}\n\nfunc (x *Sub) GetName() string {\n\tif x != nil {\n\t\treturn x.Name\n\t}\n\treturn \"\"\n}\n\nvar File_test_proto protoreflect.FileDescriptor\n\nvar file_test_proto_rawDesc = []byte{\n\t0x0a, 0x0a, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x07, 0x62, 0x69,\n\t0x6e, 0x64, 0x69, 0x6e, 0x67, 0x1a, 0x20, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72,\n\t0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x5f, 0x6d, 0x61, 0x73,\n\t0x6b, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xe2, 0x02, 0x0a, 0x0c, 0x48, 0x65, 0x6c, 0x6c,\n\t0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65,\n\t0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1e, 0x0a, 0x03,\n\t0x73, 0x75, 0x62, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x62, 0x69, 0x6e, 0x64,\n\t0x69, 0x6e, 0x67, 0x2e, 0x53, 0x75, 0x62, 0x52, 0x03, 0x73, 0x75, 0x62, 0x12, 0x3b, 0x0a, 0x0b,\n\t0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x6d, 0x61, 0x73, 0x6b, 0x18, 0x03, 0x20, 0x01, 0x28,\n\t0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,\n\t0x62, 0x75, 0x66, 0x2e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x4d, 0x61, 0x73, 0x6b, 0x52, 0x0a, 0x75,\n\t0x70, 0x64, 0x61, 0x74, 0x65, 0x4d, 0x61, 0x73, 0x6b, 0x12, 0x20, 0x0a, 0x09, 0x6f, 0x70, 0x74,\n\t0x5f, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x48, 0x00, 0x52, 0x08,\n\t0x6f, 0x70, 0x74, 0x49, 0x6e, 0x74, 0x33, 0x32, 0x88, 0x01, 0x01, 0x12, 0x20, 0x0a, 0x09, 0x6f,\n\t0x70, 0x74, 0x5f, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x48, 0x01,\n\t0x52, 0x08, 0x6f, 0x70, 0x74, 0x49, 0x6e, 0x74, 0x36, 0x34, 0x88, 0x01, 0x01, 0x12, 0x22, 0x0a,\n\t0x0a, 0x6f, 0x70, 0x74, 0x5f, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x18, 0x06, 0x20, 0x01, 0x28,\n\t0x09, 0x48, 0x02, 0x52, 0x09, 0x6f, 0x70, 0x74, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x88, 0x01,\n\t0x01, 0x12, 0x28, 0x0a, 0x08, 0x73, 0x75, 0x62, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x18, 0x07, 0x20,\n\t0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x62, 0x69, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x2e, 0x53, 0x75,\n\t0x62, 0x52, 0x08, 0x73, 0x75, 0x62, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x12, 0x24, 0x0a, 0x0d, 0x74,\n\t0x65, 0x73, 0x74, 0x5f, 0x72, 0x65, 0x70, 0x65, 0x61, 0x74, 0x65, 0x64, 0x18, 0x08, 0x20, 0x03,\n\t0x28, 0x09, 0x52, 0x0d, 0x74, 0x65, 0x73, 0x74, 0x5f, 0x72, 0x65, 0x70, 0x65, 0x61, 0x74, 0x65,\n\t0x64, 0x42, 0x0c, 0x0a, 0x0a, 0x5f, 0x6f, 0x70, 0x74, 0x5f, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x42,\n\t0x0c, 0x0a, 0x0a, 0x5f, 0x6f, 0x70, 0x74, 0x5f, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x42, 0x0d, 0x0a,\n\t0x0b, 0x5f, 0x6f, 0x70, 0x74, 0x5f, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x22, 0x1b, 0x0a, 0x03,\n\t0x53, 0x75, 0x62, 0x12, 0x14, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28,\n\t0x09, 0x52, 0x06, 0x6e, 0x61, 0x6d, 0x69, 0x6e, 0x67, 0x42, 0x2f, 0x5a, 0x2d, 0x67, 0x69, 0x74,\n\t0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x6f, 0x2d, 0x6b, 0x72, 0x61, 0x74, 0x6f,\n\t0x73, 0x2f, 0x6b, 0x72, 0x61, 0x74, 0x6f, 0x73, 0x2f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f,\n\t0x72, 0x74, 0x2f, 0x62, 0x69, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74,\n\t0x6f, 0x33,\n}\n\nvar (\n\tfile_test_proto_rawDescOnce sync.Once\n\tfile_test_proto_rawDescData = file_test_proto_rawDesc\n)\n\nfunc file_test_proto_rawDescGZIP() []byte {\n\tfile_test_proto_rawDescOnce.Do(func() {\n\t\tfile_test_proto_rawDescData = protoimpl.X.CompressGZIP(file_test_proto_rawDescData)\n\t})\n\treturn file_test_proto_rawDescData\n}\n\nvar file_test_proto_msgTypes = make([]protoimpl.MessageInfo, 2)\nvar file_test_proto_goTypes = []interface{}{\n\t(*HelloRequest)(nil),          // 0: binding.HelloRequest\n\t(*Sub)(nil),                   // 1: binding.Sub\n\t(*fieldmaskpb.FieldMask)(nil), // 2: google.protobuf.FieldMask\n}\nvar file_test_proto_depIdxs = []int32{\n\t1, // 0: binding.HelloRequest.sub:type_name -> binding.Sub\n\t2, // 1: binding.HelloRequest.update_mask:type_name -> google.protobuf.FieldMask\n\t1, // 2: binding.HelloRequest.subField:type_name -> binding.Sub\n\t3, // [3:3] is the sub-list for method output_type\n\t3, // [3:3] is the sub-list for method input_type\n\t3, // [3:3] is the sub-list for extension type_name\n\t3, // [3:3] is the sub-list for extension extendee\n\t0, // [0:3] is the sub-list for field type_name\n}\n\nfunc init() { file_test_proto_init() }\nfunc file_test_proto_init() {\n\tif File_test_proto != nil {\n\t\treturn\n\t}\n\tif !protoimpl.UnsafeEnabled {\n\t\tfile_test_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*HelloRequest); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_test_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*Sub); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t}\n\tfile_test_proto_msgTypes[0].OneofWrappers = []interface{}{}\n\ttype x struct{}\n\tout := protoimpl.TypeBuilder{\n\t\tFile: protoimpl.DescBuilder{\n\t\t\tGoPackagePath: reflect.TypeOf(x{}).PkgPath(),\n\t\t\tRawDescriptor: file_test_proto_rawDesc,\n\t\t\tNumEnums:      0,\n\t\t\tNumMessages:   2,\n\t\t\tNumExtensions: 0,\n\t\t\tNumServices:   0,\n\t\t},\n\t\tGoTypes:           file_test_proto_goTypes,\n\t\tDependencyIndexes: file_test_proto_depIdxs,\n\t\tMessageInfos:      file_test_proto_msgTypes,\n\t}.Build()\n\tFile_test_proto = out.File\n\tfile_test_proto_rawDesc = nil\n\tfile_test_proto_goTypes = nil\n\tfile_test_proto_depIdxs = nil\n}\n"
  },
  {
    "path": "internal/testdata/binding/test.proto",
    "content": "syntax = \"proto3\";\n\npackage binding;\n\nimport \"google/protobuf/field_mask.proto\";\n\noption go_package = \"github.com/go-kratos/kratos/transport/binding\";\n\n// The request message containing the user's name.\nmessage HelloRequest {\n  string name = 1;\n  Sub sub = 2;\n  google.protobuf.FieldMask update_mask = 3;\n  optional int32 opt_int32 = 4;\n  optional int64 opt_int64 = 5;\n  optional string opt_string = 6;\n  Sub subField = 7;\n  repeated string test_repeated = 8 [json_name = \"test_repeated\"];\n}\n\nmessage Sub{\n  string name = 1 [json_name = \"naming\"];\n}\n"
  },
  {
    "path": "internal/testdata/complex/complex.pb.go",
    "content": "// Code generated by protoc-gen-go. DO NOT EDIT.\n// versions:\n// \tprotoc-gen-go v1.27.1\n// \tprotoc        v5.29.3\n// source: complex.proto\n\npackage complex\n\nimport (\n\tprotoreflect \"google.golang.org/protobuf/reflect/protoreflect\"\n\tprotoimpl \"google.golang.org/protobuf/runtime/protoimpl\"\n\tdurationpb \"google.golang.org/protobuf/types/known/durationpb\"\n\tfieldmaskpb \"google.golang.org/protobuf/types/known/fieldmaskpb\"\n\ttimestamppb \"google.golang.org/protobuf/types/known/timestamppb\"\n\twrapperspb \"google.golang.org/protobuf/types/known/wrapperspb\"\n\treflect \"reflect\"\n\tsync \"sync\"\n)\n\nconst (\n\t// Verify that this generated code is sufficiently up-to-date.\n\t_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)\n\t// Verify that runtime/protoimpl is sufficiently up-to-date.\n\t_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)\n)\n\ntype Sex int32\n\nconst (\n\tSex_man   Sex = 0\n\tSex_woman Sex = 1\n)\n\n// Enum value maps for Sex.\nvar (\n\tSex_name = map[int32]string{\n\t\t0: \"man\",\n\t\t1: \"woman\",\n\t}\n\tSex_value = map[string]int32{\n\t\t\"man\":   0,\n\t\t\"woman\": 1,\n\t}\n)\n\nfunc (x Sex) Enum() *Sex {\n\tp := new(Sex)\n\t*p = x\n\treturn p\n}\n\nfunc (x Sex) String() string {\n\treturn protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))\n}\n\nfunc (Sex) Descriptor() protoreflect.EnumDescriptor {\n\treturn file_complex_proto_enumTypes[0].Descriptor()\n}\n\nfunc (Sex) Type() protoreflect.EnumType {\n\treturn &file_complex_proto_enumTypes[0]\n}\n\nfunc (x Sex) Number() protoreflect.EnumNumber {\n\treturn protoreflect.EnumNumber(x)\n}\n\n// Deprecated: Use Sex.Descriptor instead.\nfunc (Sex) EnumDescriptor() ([]byte, []int) {\n\treturn file_complex_proto_rawDescGZIP(), []int{0}\n}\n\n// SimpleMessage represents a simple message sent to the Echo service.\ntype Complex struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\t// Id represents the message identifier.\n\tId          int64                   `protobuf:\"varint,1,opt,name=id,proto3\" json:\"id,omitempty\"`\n\tNoOne       string                  `protobuf:\"bytes,2,opt,name=no_one,json=numberOne,proto3\" json:\"no_one,omitempty\"`\n\tSimple      *Simple                 `protobuf:\"bytes,3,opt,name=simple,json=very_simple,proto3\" json:\"simple,omitempty\"`\n\tStrings     []string                `protobuf:\"bytes,4,rep,name=strings,proto3\" json:\"strings,omitempty\"`\n\tSimples     []*Simple               `protobuf:\"bytes,27,rep,name=simples,proto3\" json:\"simples,omitempty\"`\n\tB           bool                    `protobuf:\"varint,5,opt,name=b,proto3\" json:\"b,omitempty\"`\n\tSex         Sex                     `protobuf:\"varint,6,opt,name=sex,proto3,enum=testproto.Sex\" json:\"sex,omitempty\"`\n\tAge         int32                   `protobuf:\"varint,7,opt,name=age,proto3\" json:\"age,omitempty\"`\n\tA           uint32                  `protobuf:\"varint,8,opt,name=a,proto3\" json:\"a,omitempty\"`\n\tCount       uint64                  `protobuf:\"varint,9,opt,name=count,proto3\" json:\"count,omitempty\"`\n\tPrice       float32                 `protobuf:\"fixed32,10,opt,name=price,proto3\" json:\"price,omitempty\"`\n\tD           float64                 `protobuf:\"fixed64,11,opt,name=d,proto3\" json:\"d,omitempty\"`\n\tByte        []byte                  `protobuf:\"bytes,12,opt,name=byte,proto3\" json:\"byte,omitempty\"`\n\tTimestamp   *timestamppb.Timestamp  `protobuf:\"bytes,13,opt,name=timestamp,proto3\" json:\"timestamp,omitempty\"`\n\tDuration    *durationpb.Duration    `protobuf:\"bytes,14,opt,name=duration,proto3\" json:\"duration,omitempty\"`\n\tField       *fieldmaskpb.FieldMask  `protobuf:\"bytes,15,opt,name=field,proto3\" json:\"field,omitempty\"`\n\tDouble      *wrapperspb.DoubleValue `protobuf:\"bytes,16,opt,name=double,proto3\" json:\"double,omitempty\"`\n\tFloat       *wrapperspb.FloatValue  `protobuf:\"bytes,17,opt,name=float,proto3\" json:\"float,omitempty\"`\n\tInt64       *wrapperspb.Int64Value  `protobuf:\"bytes,18,opt,name=int64,proto3\" json:\"int64,omitempty\"`\n\tInt32       *wrapperspb.Int32Value  `protobuf:\"bytes,19,opt,name=int32,proto3\" json:\"int32,omitempty\"`\n\tUint64      *wrapperspb.UInt64Value `protobuf:\"bytes,20,opt,name=uint64,proto3\" json:\"uint64,omitempty\"`\n\tUint32      *wrapperspb.UInt32Value `protobuf:\"bytes,21,opt,name=uint32,proto3\" json:\"uint32,omitempty\"`\n\tBool        *wrapperspb.BoolValue   `protobuf:\"bytes,22,opt,name=bool,proto3\" json:\"bool,omitempty\"`\n\tString_     *wrapperspb.StringValue `protobuf:\"bytes,23,opt,name=string,proto3\" json:\"string,omitempty\"`\n\tBytes       *wrapperspb.BytesValue  `protobuf:\"bytes,24,opt,name=bytes,proto3\" json:\"bytes,omitempty\"`\n\tMap         map[string]string       `protobuf:\"bytes,25,rep,name=map,proto3\" json:\"map,omitempty\" protobuf_key:\"bytes,1,opt,name=key,proto3\" protobuf_val:\"bytes,2,opt,name=value,proto3\"`\n\tMapInt64Key map[int64]string        `protobuf:\"bytes,26,rep,name=map_int64_key,proto3\" json:\"map_int64_key,omitempty\" protobuf_key:\"varint,1,opt,name=key,proto3\" protobuf_val:\"bytes,2,opt,name=value,proto3\"`\n}\n\nfunc (x *Complex) Reset() {\n\t*x = Complex{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_complex_proto_msgTypes[0]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *Complex) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*Complex) ProtoMessage() {}\n\nfunc (x *Complex) ProtoReflect() protoreflect.Message {\n\tmi := &file_complex_proto_msgTypes[0]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use Complex.ProtoReflect.Descriptor instead.\nfunc (*Complex) Descriptor() ([]byte, []int) {\n\treturn file_complex_proto_rawDescGZIP(), []int{0}\n}\n\nfunc (x *Complex) GetId() int64 {\n\tif x != nil {\n\t\treturn x.Id\n\t}\n\treturn 0\n}\n\nfunc (x *Complex) GetNoOne() string {\n\tif x != nil {\n\t\treturn x.NoOne\n\t}\n\treturn \"\"\n}\n\nfunc (x *Complex) GetSimple() *Simple {\n\tif x != nil {\n\t\treturn x.Simple\n\t}\n\treturn nil\n}\n\nfunc (x *Complex) GetStrings() []string {\n\tif x != nil {\n\t\treturn x.Strings\n\t}\n\treturn nil\n}\n\nfunc (x *Complex) GetSimples() []*Simple {\n\tif x != nil {\n\t\treturn x.Simples\n\t}\n\treturn nil\n}\n\nfunc (x *Complex) GetB() bool {\n\tif x != nil {\n\t\treturn x.B\n\t}\n\treturn false\n}\n\nfunc (x *Complex) GetSex() Sex {\n\tif x != nil {\n\t\treturn x.Sex\n\t}\n\treturn Sex_man\n}\n\nfunc (x *Complex) GetAge() int32 {\n\tif x != nil {\n\t\treturn x.Age\n\t}\n\treturn 0\n}\n\nfunc (x *Complex) GetA() uint32 {\n\tif x != nil {\n\t\treturn x.A\n\t}\n\treturn 0\n}\n\nfunc (x *Complex) GetCount() uint64 {\n\tif x != nil {\n\t\treturn x.Count\n\t}\n\treturn 0\n}\n\nfunc (x *Complex) GetPrice() float32 {\n\tif x != nil {\n\t\treturn x.Price\n\t}\n\treturn 0\n}\n\nfunc (x *Complex) GetD() float64 {\n\tif x != nil {\n\t\treturn x.D\n\t}\n\treturn 0\n}\n\nfunc (x *Complex) GetByte() []byte {\n\tif x != nil {\n\t\treturn x.Byte\n\t}\n\treturn nil\n}\n\nfunc (x *Complex) GetTimestamp() *timestamppb.Timestamp {\n\tif x != nil {\n\t\treturn x.Timestamp\n\t}\n\treturn nil\n}\n\nfunc (x *Complex) GetDuration() *durationpb.Duration {\n\tif x != nil {\n\t\treturn x.Duration\n\t}\n\treturn nil\n}\n\nfunc (x *Complex) GetField() *fieldmaskpb.FieldMask {\n\tif x != nil {\n\t\treturn x.Field\n\t}\n\treturn nil\n}\n\nfunc (x *Complex) GetDouble() *wrapperspb.DoubleValue {\n\tif x != nil {\n\t\treturn x.Double\n\t}\n\treturn nil\n}\n\nfunc (x *Complex) GetFloat() *wrapperspb.FloatValue {\n\tif x != nil {\n\t\treturn x.Float\n\t}\n\treturn nil\n}\n\nfunc (x *Complex) GetInt64() *wrapperspb.Int64Value {\n\tif x != nil {\n\t\treturn x.Int64\n\t}\n\treturn nil\n}\n\nfunc (x *Complex) GetInt32() *wrapperspb.Int32Value {\n\tif x != nil {\n\t\treturn x.Int32\n\t}\n\treturn nil\n}\n\nfunc (x *Complex) GetUint64() *wrapperspb.UInt64Value {\n\tif x != nil {\n\t\treturn x.Uint64\n\t}\n\treturn nil\n}\n\nfunc (x *Complex) GetUint32() *wrapperspb.UInt32Value {\n\tif x != nil {\n\t\treturn x.Uint32\n\t}\n\treturn nil\n}\n\nfunc (x *Complex) GetBool() *wrapperspb.BoolValue {\n\tif x != nil {\n\t\treturn x.Bool\n\t}\n\treturn nil\n}\n\nfunc (x *Complex) GetString_() *wrapperspb.StringValue {\n\tif x != nil {\n\t\treturn x.String_\n\t}\n\treturn nil\n}\n\nfunc (x *Complex) GetBytes() *wrapperspb.BytesValue {\n\tif x != nil {\n\t\treturn x.Bytes\n\t}\n\treturn nil\n}\n\nfunc (x *Complex) GetMap() map[string]string {\n\tif x != nil {\n\t\treturn x.Map\n\t}\n\treturn nil\n}\n\nfunc (x *Complex) GetMapInt64Key() map[int64]string {\n\tif x != nil {\n\t\treturn x.MapInt64Key\n\t}\n\treturn nil\n}\n\ntype Simple struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\tComponent string `protobuf:\"bytes,1,opt,name=component,proto3\" json:\"component,omitempty\"`\n}\n\nfunc (x *Simple) Reset() {\n\t*x = Simple{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_complex_proto_msgTypes[1]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *Simple) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*Simple) ProtoMessage() {}\n\nfunc (x *Simple) ProtoReflect() protoreflect.Message {\n\tmi := &file_complex_proto_msgTypes[1]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use Simple.ProtoReflect.Descriptor instead.\nfunc (*Simple) Descriptor() ([]byte, []int) {\n\treturn file_complex_proto_rawDescGZIP(), []int{1}\n}\n\nfunc (x *Simple) GetComponent() string {\n\tif x != nil {\n\t\treturn x.Component\n\t}\n\treturn \"\"\n}\n\nvar File_complex_proto protoreflect.FileDescriptor\n\nvar file_complex_proto_rawDesc = []byte{\n\t0x0a, 0x0d, 0x63, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x78, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12,\n\t0x09, 0x74, 0x65, 0x73, 0x74, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67,\n\t0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65,\n\t0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1e, 0x67, 0x6f, 0x6f,\n\t0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x64, 0x75, 0x72,\n\t0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x20, 0x67, 0x6f, 0x6f,\n\t0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x66, 0x69, 0x65,\n\t0x6c, 0x64, 0x5f, 0x6d, 0x61, 0x73, 0x6b, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1e, 0x67,\n\t0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x77,\n\t0x72, 0x61, 0x70, 0x70, 0x65, 0x72, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xb2, 0x09,\n\t0x0a, 0x07, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x78, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18,\n\t0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x02, 0x69, 0x64, 0x12, 0x19, 0x0a, 0x06, 0x6e, 0x6f, 0x5f,\n\t0x6f, 0x6e, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6e, 0x75, 0x6d, 0x62, 0x65,\n\t0x72, 0x4f, 0x6e, 0x65, 0x12, 0x2e, 0x0a, 0x06, 0x73, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x18, 0x03,\n\t0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x70, 0x72, 0x6f, 0x74, 0x6f,\n\t0x2e, 0x53, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x52, 0x0b, 0x76, 0x65, 0x72, 0x79, 0x5f, 0x73, 0x69,\n\t0x6d, 0x70, 0x6c, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x73, 0x18,\n\t0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x2b,\n\t0x0a, 0x07, 0x73, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x73, 0x18, 0x1b, 0x20, 0x03, 0x28, 0x0b, 0x32,\n\t0x11, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x53, 0x69, 0x6d, 0x70,\n\t0x6c, 0x65, 0x52, 0x07, 0x73, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x73, 0x12, 0x0c, 0x0a, 0x01, 0x62,\n\t0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x01, 0x62, 0x12, 0x20, 0x0a, 0x03, 0x73, 0x65, 0x78,\n\t0x18, 0x06, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x0e, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x70, 0x72, 0x6f,\n\t0x74, 0x6f, 0x2e, 0x73, 0x65, 0x78, 0x52, 0x03, 0x73, 0x65, 0x78, 0x12, 0x10, 0x0a, 0x03, 0x61,\n\t0x67, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x05, 0x52, 0x03, 0x61, 0x67, 0x65, 0x12, 0x0c, 0x0a,\n\t0x01, 0x61, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x01, 0x61, 0x12, 0x14, 0x0a, 0x05, 0x63,\n\t0x6f, 0x75, 0x6e, 0x74, 0x18, 0x09, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x63, 0x6f, 0x75, 0x6e,\n\t0x74, 0x12, 0x14, 0x0a, 0x05, 0x70, 0x72, 0x69, 0x63, 0x65, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x02,\n\t0x52, 0x05, 0x70, 0x72, 0x69, 0x63, 0x65, 0x12, 0x0c, 0x0a, 0x01, 0x64, 0x18, 0x0b, 0x20, 0x01,\n\t0x28, 0x01, 0x52, 0x01, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x62, 0x79, 0x74, 0x65, 0x18, 0x0c, 0x20,\n\t0x01, 0x28, 0x0c, 0x52, 0x04, 0x62, 0x79, 0x74, 0x65, 0x12, 0x38, 0x0a, 0x09, 0x74, 0x69, 0x6d,\n\t0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67,\n\t0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54,\n\t0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74,\n\t0x61, 0x6d, 0x70, 0x12, 0x35, 0x0a, 0x08, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18,\n\t0x0e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70,\n\t0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e,\n\t0x52, 0x08, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x30, 0x0a, 0x05, 0x66, 0x69,\n\t0x65, 0x6c, 0x64, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67,\n\t0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x46, 0x69, 0x65, 0x6c,\n\t0x64, 0x4d, 0x61, 0x73, 0x6b, 0x52, 0x05, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x12, 0x34, 0x0a, 0x06,\n\t0x64, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x18, 0x10, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67,\n\t0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44,\n\t0x6f, 0x75, 0x62, 0x6c, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x06, 0x64, 0x6f, 0x75, 0x62,\n\t0x6c, 0x65, 0x12, 0x31, 0x0a, 0x05, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x18, 0x11, 0x20, 0x01, 0x28,\n\t0x0b, 0x32, 0x1b, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,\n\t0x62, 0x75, 0x66, 0x2e, 0x46, 0x6c, 0x6f, 0x61, 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x05,\n\t0x66, 0x6c, 0x6f, 0x61, 0x74, 0x12, 0x31, 0x0a, 0x05, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x18, 0x12,\n\t0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72,\n\t0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x49, 0x6e, 0x74, 0x36, 0x34, 0x56, 0x61, 0x6c, 0x75,\n\t0x65, 0x52, 0x05, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x12, 0x31, 0x0a, 0x05, 0x69, 0x6e, 0x74, 0x33,\n\t0x32, 0x18, 0x13, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65,\n\t0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x49, 0x6e, 0x74, 0x33, 0x32, 0x56,\n\t0x61, 0x6c, 0x75, 0x65, 0x52, 0x05, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x12, 0x34, 0x0a, 0x06, 0x75,\n\t0x69, 0x6e, 0x74, 0x36, 0x34, 0x18, 0x14, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x6f,\n\t0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x55, 0x49,\n\t0x6e, 0x74, 0x36, 0x34, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x06, 0x75, 0x69, 0x6e, 0x74, 0x36,\n\t0x34, 0x12, 0x34, 0x0a, 0x06, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x18, 0x15, 0x20, 0x01, 0x28,\n\t0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,\n\t0x62, 0x75, 0x66, 0x2e, 0x55, 0x49, 0x6e, 0x74, 0x33, 0x32, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52,\n\t0x06, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x12, 0x2e, 0x0a, 0x04, 0x62, 0x6f, 0x6f, 0x6c, 0x18,\n\t0x16, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70,\n\t0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x42, 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75,\n\t0x65, 0x52, 0x04, 0x62, 0x6f, 0x6f, 0x6c, 0x12, 0x34, 0x0a, 0x06, 0x73, 0x74, 0x72, 0x69, 0x6e,\n\t0x67, 0x18, 0x17, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65,\n\t0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67,\n\t0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x06, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x12, 0x31, 0x0a,\n\t0x05, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x18, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x67,\n\t0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x42,\n\t0x79, 0x74, 0x65, 0x73, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x05, 0x62, 0x79, 0x74, 0x65, 0x73,\n\t0x12, 0x2d, 0x0a, 0x03, 0x6d, 0x61, 0x70, 0x18, 0x19, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e,\n\t0x74, 0x65, 0x73, 0x74, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65,\n\t0x78, 0x2e, 0x4d, 0x61, 0x70, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x03, 0x6d, 0x61, 0x70, 0x12,\n\t0x49, 0x0a, 0x0d, 0x6d, 0x61, 0x70, 0x5f, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x5f, 0x6b, 0x65, 0x79,\n\t0x18, 0x1a, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x70, 0x72, 0x6f,\n\t0x74, 0x6f, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x78, 0x2e, 0x4d, 0x61, 0x70, 0x49, 0x6e,\n\t0x74, 0x36, 0x34, 0x4b, 0x65, 0x79, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0d, 0x6d, 0x61, 0x70,\n\t0x5f, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x5f, 0x6b, 0x65, 0x79, 0x1a, 0x36, 0x0a, 0x08, 0x4d, 0x61,\n\t0x70, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20,\n\t0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75,\n\t0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02,\n\t0x38, 0x01, 0x1a, 0x3e, 0x0a, 0x10, 0x4d, 0x61, 0x70, 0x49, 0x6e, 0x74, 0x36, 0x34, 0x4b, 0x65,\n\t0x79, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20,\n\t0x01, 0x28, 0x03, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75,\n\t0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02,\n\t0x38, 0x01, 0x22, 0x26, 0x0a, 0x06, 0x53, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x12, 0x1c, 0x0a, 0x09,\n\t0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52,\n\t0x09, 0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x2a, 0x19, 0x0a, 0x03, 0x73, 0x65,\n\t0x78, 0x12, 0x07, 0x0a, 0x03, 0x6d, 0x61, 0x6e, 0x10, 0x00, 0x12, 0x09, 0x0a, 0x05, 0x77, 0x6f,\n\t0x6d, 0x61, 0x6e, 0x10, 0x01, 0x42, 0x57, 0x5a, 0x55, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e,\n\t0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x6f, 0x2d, 0x6b, 0x72, 0x61, 0x74, 0x6f, 0x73, 0x2f, 0x6b, 0x72,\n\t0x61, 0x74, 0x6f, 0x73, 0x2f, 0x63, 0x6d, 0x64, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x2d,\n\t0x67, 0x65, 0x6e, 0x2d, 0x67, 0x6f, 0x2d, 0x68, 0x74, 0x74, 0x70, 0x2f, 0x69, 0x6e, 0x74, 0x65,\n\t0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x2f, 0x63, 0x6f,\n\t0x6d, 0x70, 0x6c, 0x65, 0x78, 0x2f, 0x3b, 0x63, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x78, 0x62, 0x06,\n\t0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,\n}\n\nvar (\n\tfile_complex_proto_rawDescOnce sync.Once\n\tfile_complex_proto_rawDescData = file_complex_proto_rawDesc\n)\n\nfunc file_complex_proto_rawDescGZIP() []byte {\n\tfile_complex_proto_rawDescOnce.Do(func() {\n\t\tfile_complex_proto_rawDescData = protoimpl.X.CompressGZIP(file_complex_proto_rawDescData)\n\t})\n\treturn file_complex_proto_rawDescData\n}\n\nvar file_complex_proto_enumTypes = make([]protoimpl.EnumInfo, 1)\nvar file_complex_proto_msgTypes = make([]protoimpl.MessageInfo, 4)\nvar file_complex_proto_goTypes = []interface{}{\n\t(Sex)(0),                       // 0: testproto.sex\n\t(*Complex)(nil),                // 1: testproto.Complex\n\t(*Simple)(nil),                 // 2: testproto.Simple\n\tnil,                            // 3: testproto.Complex.MapEntry\n\tnil,                            // 4: testproto.Complex.MapInt64KeyEntry\n\t(*timestamppb.Timestamp)(nil),  // 5: google.protobuf.Timestamp\n\t(*durationpb.Duration)(nil),    // 6: google.protobuf.Duration\n\t(*fieldmaskpb.FieldMask)(nil),  // 7: google.protobuf.FieldMask\n\t(*wrapperspb.DoubleValue)(nil), // 8: google.protobuf.DoubleValue\n\t(*wrapperspb.FloatValue)(nil),  // 9: google.protobuf.FloatValue\n\t(*wrapperspb.Int64Value)(nil),  // 10: google.protobuf.Int64Value\n\t(*wrapperspb.Int32Value)(nil),  // 11: google.protobuf.Int32Value\n\t(*wrapperspb.UInt64Value)(nil), // 12: google.protobuf.UInt64Value\n\t(*wrapperspb.UInt32Value)(nil), // 13: google.protobuf.UInt32Value\n\t(*wrapperspb.BoolValue)(nil),   // 14: google.protobuf.BoolValue\n\t(*wrapperspb.StringValue)(nil), // 15: google.protobuf.StringValue\n\t(*wrapperspb.BytesValue)(nil),  // 16: google.protobuf.BytesValue\n}\nvar file_complex_proto_depIdxs = []int32{\n\t2,  // 0: testproto.Complex.simple:type_name -> testproto.Simple\n\t2,  // 1: testproto.Complex.simples:type_name -> testproto.Simple\n\t0,  // 2: testproto.Complex.sex:type_name -> testproto.sex\n\t5,  // 3: testproto.Complex.timestamp:type_name -> google.protobuf.Timestamp\n\t6,  // 4: testproto.Complex.duration:type_name -> google.protobuf.Duration\n\t7,  // 5: testproto.Complex.field:type_name -> google.protobuf.FieldMask\n\t8,  // 6: testproto.Complex.double:type_name -> google.protobuf.DoubleValue\n\t9,  // 7: testproto.Complex.float:type_name -> google.protobuf.FloatValue\n\t10, // 8: testproto.Complex.int64:type_name -> google.protobuf.Int64Value\n\t11, // 9: testproto.Complex.int32:type_name -> google.protobuf.Int32Value\n\t12, // 10: testproto.Complex.uint64:type_name -> google.protobuf.UInt64Value\n\t13, // 11: testproto.Complex.uint32:type_name -> google.protobuf.UInt32Value\n\t14, // 12: testproto.Complex.bool:type_name -> google.protobuf.BoolValue\n\t15, // 13: testproto.Complex.string:type_name -> google.protobuf.StringValue\n\t16, // 14: testproto.Complex.bytes:type_name -> google.protobuf.BytesValue\n\t3,  // 15: testproto.Complex.map:type_name -> testproto.Complex.MapEntry\n\t4,  // 16: testproto.Complex.map_int64_key:type_name -> testproto.Complex.MapInt64KeyEntry\n\t17, // [17:17] is the sub-list for method output_type\n\t17, // [17:17] is the sub-list for method input_type\n\t17, // [17:17] is the sub-list for extension type_name\n\t17, // [17:17] is the sub-list for extension extendee\n\t0,  // [0:17] is the sub-list for field type_name\n}\n\nfunc init() { file_complex_proto_init() }\nfunc file_complex_proto_init() {\n\tif File_complex_proto != nil {\n\t\treturn\n\t}\n\tif !protoimpl.UnsafeEnabled {\n\t\tfile_complex_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*Complex); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_complex_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*Simple); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t}\n\ttype x struct{}\n\tout := protoimpl.TypeBuilder{\n\t\tFile: protoimpl.DescBuilder{\n\t\t\tGoPackagePath: reflect.TypeOf(x{}).PkgPath(),\n\t\t\tRawDescriptor: file_complex_proto_rawDesc,\n\t\t\tNumEnums:      1,\n\t\t\tNumMessages:   4,\n\t\t\tNumExtensions: 0,\n\t\t\tNumServices:   0,\n\t\t},\n\t\tGoTypes:           file_complex_proto_goTypes,\n\t\tDependencyIndexes: file_complex_proto_depIdxs,\n\t\tEnumInfos:         file_complex_proto_enumTypes,\n\t\tMessageInfos:      file_complex_proto_msgTypes,\n\t}.Build()\n\tFile_complex_proto = out.File\n\tfile_complex_proto_rawDesc = nil\n\tfile_complex_proto_goTypes = nil\n\tfile_complex_proto_depIdxs = nil\n}\n"
  },
  {
    "path": "internal/testdata/complex/complex.proto",
    "content": "syntax = \"proto3\";\n\noption go_package = \"github.com/go-kratos/kratos/cmd/protoc-gen-go-http/internal/encoding/complex/;complex\";\n\npackage testproto;\n\nimport \"google/protobuf/timestamp.proto\";\nimport \"google/protobuf/duration.proto\";\nimport \"google/protobuf/field_mask.proto\";\nimport \"google/protobuf/wrappers.proto\";\n\n// SimpleMessage represents a simple message sent to the Echo service.\nmessage Complex {\n  // Id represents the message identifier.\n  int64 id = 1;\n  string no_one = 2 [json_name = \"numberOne\"];\n  Simple simple = 3 [json_name = \"very_simple\"];\n  repeated string strings = 4;\n  repeated Simple simples = 27;\n  bool b = 5;\n  sex sex = 6;\n  int32 age = 7;\n  uint32 a = 8;\n  uint64 count = 9;\n  float price = 10;\n  double d = 11;\n  bytes byte = 12;\n\n  google.protobuf.Timestamp timestamp = 13;\n  google.protobuf.Duration duration = 14;\n  google.protobuf.FieldMask field = 15;\n\n  google.protobuf.DoubleValue double = 16;\n  google.protobuf.FloatValue float = 17;\n  google.protobuf.Int64Value int64 = 18;\n  google.protobuf.Int32Value int32 = 19;\n  google.protobuf.UInt64Value uint64 = 20;\n  google.protobuf.UInt32Value uint32 = 21;\n  google.protobuf.BoolValue bool = 22;\n  google.protobuf.StringValue string = 23;\n  google.protobuf.BytesValue bytes = 24;\n\n  map<string, string> map = 25;\n  map<int64, string> map_int64_key = 26 [json_name = \"map_int64_key\"];\n}\n\nmessage Simple {\n  string component = 1;\n}\n\nenum sex {\n  man = 0;\n  woman = 1;\n}\n"
  },
  {
    "path": "internal/testdata/complex/generate.go",
    "content": "package complex\n\n//go:generate protoc -I . --go_out=paths=source_relative:. ./complex.proto\n"
  },
  {
    "path": "internal/testdata/encoding/test.pb.go",
    "content": "// Code generated by protoc-gen-go. DO NOT EDIT.\n// versions:\n// \tprotoc-gen-go v1.27.1\n// \tprotoc        v3.19.4\n// source: encoding/test.proto\n\npackage encoding\n\nimport (\n\tprotoreflect \"google.golang.org/protobuf/reflect/protoreflect\"\n\tprotoimpl \"google.golang.org/protobuf/runtime/protoimpl\"\n\tstructpb \"google.golang.org/protobuf/types/known/structpb\"\n\treflect \"reflect\"\n\tsync \"sync\"\n)\n\nconst (\n\t// Verify that this generated code is sufficiently up-to-date.\n\t_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)\n\t// Verify that runtime/protoimpl is sufficiently up-to-date.\n\t_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)\n)\n\ntype TestModel struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\tId    int64             `protobuf:\"varint,1,opt,name=id,proto3\" json:\"id,omitempty\"`\n\tName  string            `protobuf:\"bytes,2,opt,name=name,proto3\" json:\"name,omitempty\"`\n\tHobby []string          `protobuf:\"bytes,3,rep,name=hobby,proto3\" json:\"hobby,omitempty\"`\n\tAttrs map[string]string `protobuf:\"bytes,4,rep,name=attrs,proto3\" json:\"attrs,omitempty\" protobuf_key:\"bytes,1,opt,name=key,proto3\" protobuf_val:\"bytes,2,opt,name=value,proto3\"`\n}\n\nfunc (x *TestModel) Reset() {\n\t*x = TestModel{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_encoding_test_proto_msgTypes[0]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *TestModel) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*TestModel) ProtoMessage() {}\n\nfunc (x *TestModel) ProtoReflect() protoreflect.Message {\n\tmi := &file_encoding_test_proto_msgTypes[0]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use TestModel.ProtoReflect.Descriptor instead.\nfunc (*TestModel) Descriptor() ([]byte, []int) {\n\treturn file_encoding_test_proto_rawDescGZIP(), []int{0}\n}\n\nfunc (x *TestModel) GetId() int64 {\n\tif x != nil {\n\t\treturn x.Id\n\t}\n\treturn 0\n}\n\nfunc (x *TestModel) GetName() string {\n\tif x != nil {\n\t\treturn x.Name\n\t}\n\treturn \"\"\n}\n\nfunc (x *TestModel) GetHobby() []string {\n\tif x != nil {\n\t\treturn x.Hobby\n\t}\n\treturn nil\n}\n\nfunc (x *TestModel) GetAttrs() map[string]string {\n\tif x != nil {\n\t\treturn x.Attrs\n\t}\n\treturn nil\n}\n\ntype StructPb struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\tData     *structpb.Struct   `protobuf:\"bytes,1,opt,name=data,proto3\" json:\"data,omitempty\"`\n\tDataList []*structpb.Struct `protobuf:\"bytes,2,rep,name=data_list,json=dataList,proto3\" json:\"data_list,omitempty\"`\n}\n\nfunc (x *StructPb) Reset() {\n\t*x = StructPb{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_encoding_test_proto_msgTypes[1]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *StructPb) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*StructPb) ProtoMessage() {}\n\nfunc (x *StructPb) ProtoReflect() protoreflect.Message {\n\tmi := &file_encoding_test_proto_msgTypes[1]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use StructPb.ProtoReflect.Descriptor instead.\nfunc (*StructPb) Descriptor() ([]byte, []int) {\n\treturn file_encoding_test_proto_rawDescGZIP(), []int{1}\n}\n\nfunc (x *StructPb) GetData() *structpb.Struct {\n\tif x != nil {\n\t\treturn x.Data\n\t}\n\treturn nil\n}\n\nfunc (x *StructPb) GetDataList() []*structpb.Struct {\n\tif x != nil {\n\t\treturn x.DataList\n\t}\n\treturn nil\n}\n\nvar File_encoding_test_proto protoreflect.FileDescriptor\n\nvar file_encoding_test_proto_rawDesc = []byte{\n\t0x0a, 0x13, 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x2f, 0x74, 0x65, 0x73, 0x74, 0x2e,\n\t0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x04, 0x74, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x67, 0x6f, 0x6f,\n\t0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x73, 0x74, 0x72,\n\t0x75, 0x63, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xb3, 0x01, 0x0a, 0x0a, 0x74, 0x65,\n\t0x73, 0x74, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01,\n\t0x20, 0x01, 0x28, 0x03, 0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65,\n\t0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05,\n\t0x68, 0x6f, 0x62, 0x62, 0x79, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x68, 0x6f, 0x62,\n\t0x62, 0x79, 0x12, 0x31, 0x0a, 0x05, 0x61, 0x74, 0x74, 0x72, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28,\n\t0x0b, 0x32, 0x1b, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x5f, 0x6d, 0x6f,\n\t0x64, 0x65, 0x6c, 0x2e, 0x41, 0x74, 0x74, 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x05,\n\t0x61, 0x74, 0x74, 0x72, 0x73, 0x1a, 0x38, 0x0a, 0x0a, 0x41, 0x74, 0x74, 0x72, 0x73, 0x45, 0x6e,\n\t0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,\n\t0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02,\n\t0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22,\n\t0x6d, 0x0a, 0x08, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x50, 0x62, 0x12, 0x2b, 0x0a, 0x04, 0x64,\n\t0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67,\n\t0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75,\n\t0x63, 0x74, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x12, 0x34, 0x0a, 0x09, 0x64, 0x61, 0x74, 0x61,\n\t0x5f, 0x6c, 0x69, 0x73, 0x74, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f,\n\t0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74,\n\t0x72, 0x75, 0x63, 0x74, 0x52, 0x08, 0x64, 0x61, 0x74, 0x61, 0x4c, 0x69, 0x73, 0x74, 0x42, 0x0d,\n\t0x5a, 0x0b, 0x2e, 0x2e, 0x2f, 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x62, 0x06, 0x70,\n\t0x72, 0x6f, 0x74, 0x6f, 0x33,\n}\n\nvar (\n\tfile_encoding_test_proto_rawDescOnce sync.Once\n\tfile_encoding_test_proto_rawDescData = file_encoding_test_proto_rawDesc\n)\n\nfunc file_encoding_test_proto_rawDescGZIP() []byte {\n\tfile_encoding_test_proto_rawDescOnce.Do(func() {\n\t\tfile_encoding_test_proto_rawDescData = protoimpl.X.CompressGZIP(file_encoding_test_proto_rawDescData)\n\t})\n\treturn file_encoding_test_proto_rawDescData\n}\n\nvar file_encoding_test_proto_msgTypes = make([]protoimpl.MessageInfo, 3)\nvar file_encoding_test_proto_goTypes = []interface{}{\n\t(*TestModel)(nil),       // 0: test.test_model\n\t(*StructPb)(nil),        // 1: test.StructPb\n\tnil,                     // 2: test.test_model.AttrsEntry\n\t(*structpb.Struct)(nil), // 3: google.protobuf.Struct\n}\nvar file_encoding_test_proto_depIdxs = []int32{\n\t2, // 0: test.test_model.attrs:type_name -> test.test_model.AttrsEntry\n\t3, // 1: test.StructPb.data:type_name -> google.protobuf.Struct\n\t3, // 2: test.StructPb.data_list:type_name -> google.protobuf.Struct\n\t3, // [3:3] is the sub-list for method output_type\n\t3, // [3:3] is the sub-list for method input_type\n\t3, // [3:3] is the sub-list for extension type_name\n\t3, // [3:3] is the sub-list for extension extendee\n\t0, // [0:3] is the sub-list for field type_name\n}\n\nfunc init() { file_encoding_test_proto_init() }\nfunc file_encoding_test_proto_init() {\n\tif File_encoding_test_proto != nil {\n\t\treturn\n\t}\n\tif !protoimpl.UnsafeEnabled {\n\t\tfile_encoding_test_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*TestModel); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_encoding_test_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*StructPb); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t}\n\ttype x struct{}\n\tout := protoimpl.TypeBuilder{\n\t\tFile: protoimpl.DescBuilder{\n\t\t\tGoPackagePath: reflect.TypeOf(x{}).PkgPath(),\n\t\t\tRawDescriptor: file_encoding_test_proto_rawDesc,\n\t\t\tNumEnums:      0,\n\t\t\tNumMessages:   3,\n\t\t\tNumExtensions: 0,\n\t\t\tNumServices:   0,\n\t\t},\n\t\tGoTypes:           file_encoding_test_proto_goTypes,\n\t\tDependencyIndexes: file_encoding_test_proto_depIdxs,\n\t\tMessageInfos:      file_encoding_test_proto_msgTypes,\n\t}.Build()\n\tFile_encoding_test_proto = out.File\n\tfile_encoding_test_proto_rawDesc = nil\n\tfile_encoding_test_proto_goTypes = nil\n\tfile_encoding_test_proto_depIdxs = nil\n}\n"
  },
  {
    "path": "internal/testdata/encoding/test.proto",
    "content": "syntax = \"proto3\";\n\npackage test;\n\noption go_package = \"../encoding\";\n\nimport \"google/protobuf/struct.proto\";\n\nmessage test_model {\n  int64 id = 1;\n  string name = 2;\n  repeated string hobby = 3;\n  map<string, string> attrs = 4;\n}\n\nmessage StructPb {\n  google.protobuf.Struct data = 1;\n  repeated  google.protobuf.Struct data_list = 2;\n}\n"
  },
  {
    "path": "internal/testdata/helloworld/generate.go",
    "content": "package helloworld\n\n//go:generate protoc -I . -I ../../../third_party --go_out=paths=source_relative:. --go-grpc_out=paths=source_relative:. --go-http_out=paths=source_relative:. ./helloworld.proto\n"
  },
  {
    "path": "internal/testdata/helloworld/helloworld.pb.go",
    "content": "// Code generated by protoc-gen-go. DO NOT EDIT.\n// versions:\n// \tprotoc-gen-go v1.28.0\n// \tprotoc        v3.17.3\n// source: helloworld.proto\n\npackage helloworld\n\nimport (\n\t_ \"google.golang.org/genproto/googleapis/api/annotations\"\n\tprotoreflect \"google.golang.org/protobuf/reflect/protoreflect\"\n\tprotoimpl \"google.golang.org/protobuf/runtime/protoimpl\"\n\treflect \"reflect\"\n\tsync \"sync\"\n)\n\nconst (\n\t// Verify that this generated code is sufficiently up-to-date.\n\t_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)\n\t// Verify that runtime/protoimpl is sufficiently up-to-date.\n\t_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)\n)\n\n// The request message containing the user's name.\ntype HelloRequest struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\tName string `protobuf:\"bytes,1,opt,name=name,proto3\" json:\"name,omitempty\"`\n}\n\nfunc (x *HelloRequest) Reset() {\n\t*x = HelloRequest{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_helloworld_proto_msgTypes[0]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *HelloRequest) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*HelloRequest) ProtoMessage() {}\n\nfunc (x *HelloRequest) ProtoReflect() protoreflect.Message {\n\tmi := &file_helloworld_proto_msgTypes[0]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use HelloRequest.ProtoReflect.Descriptor instead.\nfunc (*HelloRequest) Descriptor() ([]byte, []int) {\n\treturn file_helloworld_proto_rawDescGZIP(), []int{0}\n}\n\nfunc (x *HelloRequest) GetName() string {\n\tif x != nil {\n\t\treturn x.Name\n\t}\n\treturn \"\"\n}\n\n// The response message containing the greetings\ntype HelloReply struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\tMessage string `protobuf:\"bytes,1,opt,name=message,proto3\" json:\"message,omitempty\"`\n}\n\nfunc (x *HelloReply) Reset() {\n\t*x = HelloReply{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_helloworld_proto_msgTypes[1]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *HelloReply) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*HelloReply) ProtoMessage() {}\n\nfunc (x *HelloReply) ProtoReflect() protoreflect.Message {\n\tmi := &file_helloworld_proto_msgTypes[1]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use HelloReply.ProtoReflect.Descriptor instead.\nfunc (*HelloReply) Descriptor() ([]byte, []int) {\n\treturn file_helloworld_proto_rawDescGZIP(), []int{1}\n}\n\nfunc (x *HelloReply) GetMessage() string {\n\tif x != nil {\n\t\treturn x.Message\n\t}\n\treturn \"\"\n}\n\nvar File_helloworld_proto protoreflect.FileDescriptor\n\nvar file_helloworld_proto_rawDesc = []byte{\n\t0x0a, 0x10, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x2e, 0x70, 0x72, 0x6f,\n\t0x74, 0x6f, 0x12, 0x0a, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x1a, 0x1c,\n\t0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x61, 0x6e, 0x6e, 0x6f, 0x74,\n\t0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x22, 0x0a, 0x0c,\n\t0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04,\n\t0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65,\n\t0x22, 0x26, 0x0a, 0x0a, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x18,\n\t0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52,\n\t0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x32, 0xab, 0x01, 0x0a, 0x07, 0x47, 0x72, 0x65,\n\t0x65, 0x74, 0x65, 0x72, 0x12, 0x58, 0x0a, 0x08, 0x53, 0x61, 0x79, 0x48, 0x65, 0x6c, 0x6c, 0x6f,\n\t0x12, 0x18, 0x2e, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x2e, 0x48, 0x65,\n\t0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x68, 0x65, 0x6c,\n\t0x6c, 0x6f, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x2e, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x70,\n\t0x6c, 0x79, 0x22, 0x1a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x14, 0x12, 0x12, 0x2f, 0x68, 0x65, 0x6c,\n\t0x6c, 0x6f, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x2f, 0x7b, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x12, 0x46,\n\t0x0a, 0x0e, 0x53, 0x61, 0x79, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d,\n\t0x12, 0x18, 0x2e, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x2e, 0x48, 0x65,\n\t0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x68, 0x65, 0x6c,\n\t0x6c, 0x6f, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x2e, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x70,\n\t0x6c, 0x79, 0x28, 0x01, 0x30, 0x01, 0x42, 0x3d, 0x5a, 0x3b, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62,\n\t0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x6f, 0x2d, 0x6b, 0x72, 0x61, 0x74, 0x6f, 0x73, 0x2f, 0x6b,\n\t0x72, 0x61, 0x74, 0x6f, 0x73, 0x2f, 0x76, 0x32, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61,\n\t0x6c, 0x2f, 0x74, 0x65, 0x73, 0x74, 0x64, 0x61, 0x74, 0x61, 0x2f, 0x68, 0x65, 0x6c, 0x6c, 0x6f,\n\t0x77, 0x6f, 0x72, 0x6c, 0x64, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,\n}\n\nvar (\n\tfile_helloworld_proto_rawDescOnce sync.Once\n\tfile_helloworld_proto_rawDescData = file_helloworld_proto_rawDesc\n)\n\nfunc file_helloworld_proto_rawDescGZIP() []byte {\n\tfile_helloworld_proto_rawDescOnce.Do(func() {\n\t\tfile_helloworld_proto_rawDescData = protoimpl.X.CompressGZIP(file_helloworld_proto_rawDescData)\n\t})\n\treturn file_helloworld_proto_rawDescData\n}\n\nvar file_helloworld_proto_msgTypes = make([]protoimpl.MessageInfo, 2)\nvar file_helloworld_proto_goTypes = []interface{}{\n\t(*HelloRequest)(nil), // 0: helloworld.HelloRequest\n\t(*HelloReply)(nil),   // 1: helloworld.HelloReply\n}\nvar file_helloworld_proto_depIdxs = []int32{\n\t0, // 0: helloworld.Greeter.SayHello:input_type -> helloworld.HelloRequest\n\t0, // 1: helloworld.Greeter.SayHelloStream:input_type -> helloworld.HelloRequest\n\t1, // 2: helloworld.Greeter.SayHello:output_type -> helloworld.HelloReply\n\t1, // 3: helloworld.Greeter.SayHelloStream:output_type -> helloworld.HelloReply\n\t2, // [2:4] is the sub-list for method output_type\n\t0, // [0:2] is the sub-list for method input_type\n\t0, // [0:0] is the sub-list for extension type_name\n\t0, // [0:0] is the sub-list for extension extendee\n\t0, // [0:0] is the sub-list for field type_name\n}\n\nfunc init() { file_helloworld_proto_init() }\nfunc file_helloworld_proto_init() {\n\tif File_helloworld_proto != nil {\n\t\treturn\n\t}\n\tif !protoimpl.UnsafeEnabled {\n\t\tfile_helloworld_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*HelloRequest); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_helloworld_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*HelloReply); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t}\n\ttype x struct{}\n\tout := protoimpl.TypeBuilder{\n\t\tFile: protoimpl.DescBuilder{\n\t\t\tGoPackagePath: reflect.TypeOf(x{}).PkgPath(),\n\t\t\tRawDescriptor: file_helloworld_proto_rawDesc,\n\t\t\tNumEnums:      0,\n\t\t\tNumMessages:   2,\n\t\t\tNumExtensions: 0,\n\t\t\tNumServices:   1,\n\t\t},\n\t\tGoTypes:           file_helloworld_proto_goTypes,\n\t\tDependencyIndexes: file_helloworld_proto_depIdxs,\n\t\tMessageInfos:      file_helloworld_proto_msgTypes,\n\t}.Build()\n\tFile_helloworld_proto = out.File\n\tfile_helloworld_proto_rawDesc = nil\n\tfile_helloworld_proto_goTypes = nil\n\tfile_helloworld_proto_depIdxs = nil\n}\n"
  },
  {
    "path": "internal/testdata/helloworld/helloworld.proto",
    "content": "syntax = \"proto3\";\n\npackage helloworld;\n\nimport \"google/api/annotations.proto\";\n\noption go_package = \"github.com/go-kratos/kratos/v2/internal/testdata/helloworld\";\n\n// The greeting service definition.\nservice Greeter {\n  // Sends a greeting\n  rpc SayHello (HelloRequest) returns (HelloReply) {\n    option (google.api.http) = {\n      get: \"/helloworld/{name}\",\n    };\n  }\n  // Sends a greeting\n  rpc SayHelloStream (stream HelloRequest) returns (stream HelloReply);\n}\n\n// The request message containing the user's name.\nmessage HelloRequest {\n  string name = 1;\n}\n\n// The response message containing the greetings\nmessage HelloReply {\n  string message = 1;\n}\n"
  },
  {
    "path": "internal/testdata/helloworld/helloworld_grpc.pb.go",
    "content": "// Code generated by protoc-gen-go-grpc. DO NOT EDIT.\n// versions:\n// - protoc-gen-go-grpc v1.2.0\n// - protoc             v3.17.3\n// source: helloworld.proto\n\npackage helloworld\n\nimport (\n\tcontext \"context\"\n\tgrpc \"google.golang.org/grpc\"\n\tcodes \"google.golang.org/grpc/codes\"\n\tstatus \"google.golang.org/grpc/status\"\n)\n\n// This is a compile-time assertion to ensure that this generated file\n// is compatible with the grpc package it is being compiled against.\n// Requires gRPC-Go v1.32.0 or later.\nconst _ = grpc.SupportPackageIsVersion7\n\n// GreeterClient is the client API for Greeter service.\n//\n// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.\ntype GreeterClient interface {\n\t// Sends a greeting\n\tSayHello(ctx context.Context, in *HelloRequest, opts ...grpc.CallOption) (*HelloReply, error)\n\t// Sends a greeting\n\tSayHelloStream(ctx context.Context, opts ...grpc.CallOption) (Greeter_SayHelloStreamClient, error)\n}\n\ntype greeterClient struct {\n\tcc grpc.ClientConnInterface\n}\n\nfunc NewGreeterClient(cc grpc.ClientConnInterface) GreeterClient {\n\treturn &greeterClient{cc}\n}\n\nfunc (c *greeterClient) SayHello(ctx context.Context, in *HelloRequest, opts ...grpc.CallOption) (*HelloReply, error) {\n\tout := new(HelloReply)\n\terr := c.cc.Invoke(ctx, \"/helloworld.Greeter/SayHello\", in, out, opts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn out, nil\n}\n\nfunc (c *greeterClient) SayHelloStream(ctx context.Context, opts ...grpc.CallOption) (Greeter_SayHelloStreamClient, error) {\n\tstream, err := c.cc.NewStream(ctx, &Greeter_ServiceDesc.Streams[0], \"/helloworld.Greeter/SayHelloStream\", opts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tx := &greeterSayHelloStreamClient{stream}\n\treturn x, nil\n}\n\ntype Greeter_SayHelloStreamClient interface {\n\tSend(*HelloRequest) error\n\tRecv() (*HelloReply, error)\n\tgrpc.ClientStream\n}\n\ntype greeterSayHelloStreamClient struct {\n\tgrpc.ClientStream\n}\n\nfunc (x *greeterSayHelloStreamClient) Send(m *HelloRequest) error {\n\treturn x.ClientStream.SendMsg(m)\n}\n\nfunc (x *greeterSayHelloStreamClient) Recv() (*HelloReply, error) {\n\tm := new(HelloReply)\n\tif err := x.ClientStream.RecvMsg(m); err != nil {\n\t\treturn nil, err\n\t}\n\treturn m, nil\n}\n\n// GreeterServer is the server API for Greeter service.\n// All implementations must embed UnimplementedGreeterServer\n// for forward compatibility\ntype GreeterServer interface {\n\t// Sends a greeting\n\tSayHello(context.Context, *HelloRequest) (*HelloReply, error)\n\t// Sends a greeting\n\tSayHelloStream(Greeter_SayHelloStreamServer) error\n\tmustEmbedUnimplementedGreeterServer()\n}\n\n// UnimplementedGreeterServer must be embedded to have forward compatible implementations.\ntype UnimplementedGreeterServer struct {\n}\n\nfunc (UnimplementedGreeterServer) SayHello(context.Context, *HelloRequest) (*HelloReply, error) {\n\treturn nil, status.Errorf(codes.Unimplemented, \"method SayHello not implemented\")\n}\nfunc (UnimplementedGreeterServer) SayHelloStream(Greeter_SayHelloStreamServer) error {\n\treturn status.Errorf(codes.Unimplemented, \"method SayHelloStream not implemented\")\n}\nfunc (UnimplementedGreeterServer) mustEmbedUnimplementedGreeterServer() {}\n\n// UnsafeGreeterServer may be embedded to opt out of forward compatibility for this service.\n// Use of this interface is not recommended, as added methods to GreeterServer will\n// result in compilation errors.\ntype UnsafeGreeterServer interface {\n\tmustEmbedUnimplementedGreeterServer()\n}\n\nfunc RegisterGreeterServer(s grpc.ServiceRegistrar, srv GreeterServer) {\n\ts.RegisterService(&Greeter_ServiceDesc, srv)\n}\n\nfunc _Greeter_SayHello_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {\n\tin := new(HelloRequest)\n\tif err := dec(in); err != nil {\n\t\treturn nil, err\n\t}\n\tif interceptor == nil {\n\t\treturn srv.(GreeterServer).SayHello(ctx, in)\n\t}\n\tinfo := &grpc.UnaryServerInfo{\n\t\tServer:     srv,\n\t\tFullMethod: \"/helloworld.Greeter/SayHello\",\n\t}\n\thandler := func(ctx context.Context, req interface{}) (interface{}, error) {\n\t\treturn srv.(GreeterServer).SayHello(ctx, req.(*HelloRequest))\n\t}\n\treturn interceptor(ctx, in, info, handler)\n}\n\nfunc _Greeter_SayHelloStream_Handler(srv interface{}, stream grpc.ServerStream) error {\n\treturn srv.(GreeterServer).SayHelloStream(&greeterSayHelloStreamServer{stream})\n}\n\ntype Greeter_SayHelloStreamServer interface {\n\tSend(*HelloReply) error\n\tRecv() (*HelloRequest, error)\n\tgrpc.ServerStream\n}\n\ntype greeterSayHelloStreamServer struct {\n\tgrpc.ServerStream\n}\n\nfunc (x *greeterSayHelloStreamServer) Send(m *HelloReply) error {\n\treturn x.ServerStream.SendMsg(m)\n}\n\nfunc (x *greeterSayHelloStreamServer) Recv() (*HelloRequest, error) {\n\tm := new(HelloRequest)\n\tif err := x.ServerStream.RecvMsg(m); err != nil {\n\t\treturn nil, err\n\t}\n\treturn m, nil\n}\n\n// Greeter_ServiceDesc is the grpc.ServiceDesc for Greeter service.\n// It's only intended for direct use with grpc.RegisterService,\n// and not to be introspected or modified (even as a copy)\nvar Greeter_ServiceDesc = grpc.ServiceDesc{\n\tServiceName: \"helloworld.Greeter\",\n\tHandlerType: (*GreeterServer)(nil),\n\tMethods: []grpc.MethodDesc{\n\t\t{\n\t\t\tMethodName: \"SayHello\",\n\t\t\tHandler:    _Greeter_SayHello_Handler,\n\t\t},\n\t},\n\tStreams: []grpc.StreamDesc{\n\t\t{\n\t\t\tStreamName:    \"SayHelloStream\",\n\t\t\tHandler:       _Greeter_SayHelloStream_Handler,\n\t\t\tServerStreams: true,\n\t\t\tClientStreams: true,\n\t\t},\n\t},\n\tMetadata: \"helloworld.proto\",\n}\n"
  },
  {
    "path": "internal/testdata/helloworld/helloworld_http.pb.go",
    "content": "// Code generated by protoc-gen-go-http. DO NOT EDIT.\n// versions:\n// protoc-gen-go-http v2.3.1\n\npackage helloworld\n\nimport (\n\tcontext \"context\"\n\thttp \"github.com/go-kratos/kratos/v2/transport/http\"\n\tbinding \"github.com/go-kratos/kratos/v2/transport/http/binding\"\n)\n\n// This is a compile-time assertion to ensure that this generated file\n// is compatible with the kratos package it is being compiled against.\nvar _ = new(context.Context)\nvar _ = binding.EncodeURL\n\nconst _ = http.SupportPackageIsVersion1\n\nconst OperationGreeterSayHello = \"/helloworld.Greeter/SayHello\"\n\ntype GreeterHTTPServer interface {\n\tSayHello(context.Context, *HelloRequest) (*HelloReply, error)\n}\n\nfunc RegisterGreeterHTTPServer(s *http.Server, srv GreeterHTTPServer) {\n\tr := s.Route(\"/\")\n\tr.GET(\"/helloworld/{name}\", _Greeter_SayHello0_HTTP_Handler(srv))\n}\n\nfunc _Greeter_SayHello0_HTTP_Handler(srv GreeterHTTPServer) func(ctx http.Context) error {\n\treturn func(ctx http.Context) error {\n\t\tvar in HelloRequest\n\t\tif err := ctx.BindQuery(&in); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := ctx.BindVars(&in); err != nil {\n\t\t\treturn err\n\t\t}\n\t\thttp.SetOperation(ctx, OperationGreeterSayHello)\n\t\th := ctx.Middleware(func(ctx context.Context, req interface{}) (interface{}, error) {\n\t\t\treturn srv.SayHello(ctx, req.(*HelloRequest))\n\t\t})\n\t\tout, err := h(ctx, &in)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\treply := out.(*HelloReply)\n\t\treturn ctx.Result(200, reply)\n\t}\n}\n\ntype GreeterHTTPClient interface {\n\tSayHello(ctx context.Context, req *HelloRequest, opts ...http.CallOption) (rsp *HelloReply, err error)\n}\n\ntype GreeterHTTPClientImpl struct {\n\tcc *http.Client\n}\n\nfunc NewGreeterHTTPClient(client *http.Client) GreeterHTTPClient {\n\treturn &GreeterHTTPClientImpl{client}\n}\n\nfunc (c *GreeterHTTPClientImpl) SayHello(ctx context.Context, in *HelloRequest, opts ...http.CallOption) (*HelloReply, error) {\n\tvar out HelloReply\n\tpattern := \"/helloworld/{name}\"\n\tpath := binding.EncodeURL(pattern, in, true)\n\topts = append(opts, http.Operation(OperationGreeterSayHello))\n\topts = append(opts, http.PathTemplate(pattern))\n\terr := c.cc.Invoke(ctx, \"GET\", path, nil, &out, opts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn &out, err\n}\n"
  },
  {
    "path": "log/README.md",
    "content": "# Logger\n\n## Usage\n\n### Structured logging\n\n```go\nlogger := log.NewStdLogger(os.Stdout)\n// fields & valuer\nlogger = log.With(logger,\n    \"service.name\", \"helloworld\",\n    \"service.version\", \"v1.0.0\",\n    \"ts\", log.DefaultTimestamp,\n    \"caller\", log.DefaultCaller,\n)\nlogger.Log(log.LevelInfo, \"key\", \"value\")\n\n// helper\nhelper := log.NewHelper(logger)\nhelper.Log(log.LevelInfo, \"key\", \"value\")\nhelper.Info(\"info message\")\nhelper.Infof(\"info %s\", \"message\")\nhelper.Infow(\"key\", \"value\")\n\n// filter\nlog := log.NewHelper(log.NewFilter(logger,\n\tlog.FilterLevel(log.LevelInfo),\n\tlog.FilterKey(\"foo\"),\n\tlog.FilterValue(\"bar\"),\n\tlog.FilterFunc(customFilter),\n))\nlog.Debug(\"debug log\")\nlog.Info(\"info log\")\nlog.Warn(\"warn log\")\nlog.Error(\"warn log\")\n```\n\n## Third party log library\n\n### zap\n\n```shell\ngo get -u github.com/go-kratos/kratos/contrib/log/zap/v2\n```\n\n### logrus\n\n```shell\ngo get -u github.com/go-kratos/kratos/contrib/log/logrus/v2\n```\n\n### fluent\n\n```shell\ngo get -u github.com/go-kratos/kratos/contrib/log/fluent/v2\n```\n\n### aliyun\n\n```shell\ngo get -u github.com/go-kratos/kratos/contrib/log/aliyun/v2\n```\n"
  },
  {
    "path": "log/filter.go",
    "content": "package log\n\n// FilterOption is filter option.\ntype FilterOption func(*Filter)\n\nconst fuzzyStr = \"***\"\n\n// FilterLevel with filter level.\nfunc FilterLevel(level Level) FilterOption {\n\treturn func(opts *Filter) {\n\t\topts.level = level\n\t}\n}\n\n// FilterKey with filter key.\nfunc FilterKey(key ...string) FilterOption {\n\treturn func(o *Filter) {\n\t\tfor _, v := range key {\n\t\t\to.key[v] = struct{}{}\n\t\t}\n\t}\n}\n\n// FilterValue with filter value.\nfunc FilterValue(value ...string) FilterOption {\n\treturn func(o *Filter) {\n\t\tfor _, v := range value {\n\t\t\to.value[v] = struct{}{}\n\t\t}\n\t}\n}\n\n// FilterFunc with filter func.\nfunc FilterFunc(f func(level Level, keyvals ...any) bool) FilterOption {\n\treturn func(o *Filter) {\n\t\to.filter = f\n\t}\n}\n\n// Filter is a logger filter.\ntype Filter struct {\n\tlogger Logger\n\tlevel  Level\n\tkey    map[any]struct{}\n\tvalue  map[any]struct{}\n\tfilter func(level Level, keyvals ...any) bool\n}\n\n// NewFilter new a logger filter.\nfunc NewFilter(logger Logger, opts ...FilterOption) *Filter {\n\toptions := Filter{\n\t\tlogger: logger,\n\t\tkey:    make(map[any]struct{}),\n\t\tvalue:  make(map[any]struct{}),\n\t}\n\tfor _, o := range opts {\n\t\to(&options)\n\t}\n\treturn &options\n}\n\n// Log Print log by level and keyvals.\nfunc (f *Filter) Log(level Level, keyvals ...any) error {\n\tif level < f.level {\n\t\treturn nil\n\t}\n\t// prefixkv contains the slice of arguments defined as prefixes during the log initialization\n\tvar prefixkv []any\n\tl, ok := f.logger.(*logger)\n\tif ok && len(l.prefix) > 0 {\n\t\tprefixkv = make([]any, 0, len(l.prefix))\n\t\tprefixkv = append(prefixkv, l.prefix...)\n\t}\n\n\tif f.filter != nil && (f.filter(level, prefixkv...) || f.filter(level, keyvals...)) {\n\t\treturn nil\n\t}\n\n\tif len(f.key) > 0 || len(f.value) > 0 {\n\t\tfor i := 0; i < len(keyvals); i += 2 {\n\t\t\tv := i + 1\n\t\t\tif v >= len(keyvals) {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tif _, ok := f.key[keyvals[i]]; ok {\n\t\t\t\tkeyvals[v] = fuzzyStr\n\t\t\t}\n\t\t\tif _, ok := f.value[keyvals[v]]; ok {\n\t\t\t\tkeyvals[v] = fuzzyStr\n\t\t\t}\n\t\t}\n\t}\n\treturn f.logger.Log(level, keyvals...)\n}\n"
  },
  {
    "path": "log/filter_test.go",
    "content": "package log\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"io\"\n\t\"strings\"\n\t\"sync\"\n\t\"testing\"\n\t\"time\"\n)\n\nfunc TestFilterAll(_ *testing.T) {\n\tlogger := With(DefaultLogger, \"ts\", DefaultTimestamp, \"caller\", DefaultCaller)\n\tlog := NewHelper(NewFilter(logger,\n\t\tFilterLevel(LevelDebug),\n\t\tFilterKey(\"username\"),\n\t\tFilterValue(\"hello\"),\n\t\tFilterFunc(testFilterFunc),\n\t))\n\tlog.Log(LevelDebug, \"msg\", \"test debug\")\n\tlog.Info(\"hello\")\n\tlog.Infow(\"password\", \"123456\")\n\tlog.Infow(\"username\", \"kratos\")\n\tlog.Warn(\"warn log\")\n}\n\nfunc TestFilterLevel(_ *testing.T) {\n\tlogger := With(DefaultLogger, \"ts\", DefaultTimestamp, \"caller\", DefaultCaller)\n\tlog := NewHelper(NewFilter(NewFilter(logger, FilterLevel(LevelWarn))))\n\tlog.Log(LevelDebug, \"msg1\", \"te1st debug\")\n\tlog.Debug(\"test debug\")\n\tlog.Debugf(\"test %s\", \"debug\")\n\tlog.Debugw(\"log\", \"test debug\")\n\tlog.Warn(\"warn log\")\n}\n\nfunc TestFilterCaller(_ *testing.T) {\n\tlogger := With(DefaultLogger, \"ts\", DefaultTimestamp, \"caller\", DefaultCaller)\n\tlog := NewFilter(logger)\n\t_ = log.Log(LevelDebug, \"msg1\", \"te1st debug\")\n\tlogHelper := NewHelper(NewFilter(logger))\n\tlogHelper.Log(LevelDebug, \"msg1\", \"te1st debug\")\n}\n\nfunc TestFilterKey(_ *testing.T) {\n\tlogger := With(DefaultLogger, \"ts\", DefaultTimestamp, \"caller\", DefaultCaller)\n\tlog := NewHelper(NewFilter(logger, FilterKey(\"password\")))\n\tlog.Debugw(\"password\", \"123456\")\n}\n\nfunc TestFilterValue(_ *testing.T) {\n\tlogger := With(DefaultLogger, \"ts\", DefaultTimestamp, \"caller\", DefaultCaller)\n\tlog := NewHelper(NewFilter(logger, FilterValue(\"debug\")))\n\tlog.Debugf(\"test %s\", \"debug\")\n}\n\nfunc TestFilterFunc(_ *testing.T) {\n\tlogger := With(DefaultLogger, \"ts\", DefaultTimestamp, \"caller\", DefaultCaller)\n\tlog := NewHelper(NewFilter(logger, FilterFunc(testFilterFunc)))\n\tlog.Debug(\"debug level\")\n\tlog.Infow(\"password\", \"123456\")\n}\n\nfunc BenchmarkFilterKey(b *testing.B) {\n\tlog := NewHelper(NewFilter(NewStdLogger(io.Discard), FilterKey(\"password\")))\n\tfor i := 0; i < b.N; i++ {\n\t\tlog.Infow(\"password\", \"123456\")\n\t}\n}\n\nfunc BenchmarkFilterValue(b *testing.B) {\n\tlog := NewHelper(NewFilter(NewStdLogger(io.Discard), FilterValue(\"password\")))\n\tfor i := 0; i < b.N; i++ {\n\t\tlog.Infow(\"password\")\n\t}\n}\n\nfunc BenchmarkFilterFunc(b *testing.B) {\n\tlog := NewHelper(NewFilter(NewStdLogger(io.Discard), FilterFunc(testFilterFunc)))\n\tfor i := 0; i < b.N; i++ {\n\t\tlog.Info(\"password\", \"123456\")\n\t}\n}\n\nfunc testFilterFunc(level Level, keyvals ...any) bool {\n\tif level == LevelWarn {\n\t\treturn true\n\t}\n\tfor i := 0; i < len(keyvals); i++ {\n\t\tif keyvals[i] == \"password\" {\n\t\t\tkeyvals[i+1] = fuzzyStr\n\t\t}\n\t}\n\treturn false\n}\n\nfunc TestFilterFuncWitchLoggerPrefix(t *testing.T) {\n\tbuf := new(bytes.Buffer)\n\ttests := []struct {\n\t\tlogger Logger\n\t\twant   string\n\t}{\n\t\t{\n\t\t\tlogger: NewFilter(With(NewStdLogger(buf), \"caller\", \"caller\", \"prefix\", \"whatever\"), FilterFunc(testFilterFuncWithLoggerPrefix)),\n\t\t\twant:   \"\",\n\t\t},\n\t\t{\n\t\t\t// Filtered value\n\t\t\tlogger: NewFilter(With(NewStdLogger(buf), \"caller\", \"caller\"), FilterFunc(testFilterFuncWithLoggerPrefix)),\n\t\t\twant:   \"INFO caller=caller msg=msg filtered=***\\n\",\n\t\t},\n\t\t{\n\t\t\t// NO prefix\n\t\t\tlogger: NewFilter(With(NewStdLogger(buf)), FilterFunc(testFilterFuncWithLoggerPrefix)),\n\t\t\twant:   \"INFO msg=msg filtered=***\\n\",\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\terr := tt.logger.Log(LevelInfo, \"msg\", \"msg\", \"filtered\", \"true\")\n\t\tif err != nil {\n\t\t\tt.Fatal(\"err should be nil\")\n\t\t}\n\t\tgot := buf.String()\n\t\tif got != tt.want {\n\t\t\tt.Fatalf(\"filter should catch prefix, want %s, got %s.\", tt.want, got)\n\t\t}\n\t\tbuf.Reset()\n\t}\n}\n\nfunc testFilterFuncWithLoggerPrefix(level Level, keyvals ...any) bool {\n\tif level == LevelWarn {\n\t\treturn true\n\t}\n\tfor i := 0; i < len(keyvals); i += 2 {\n\t\tif keyvals[i] == \"prefix\" {\n\t\t\treturn true\n\t\t}\n\t\tif keyvals[i] == \"filtered\" {\n\t\t\tkeyvals[i+1] = fuzzyStr\n\t\t}\n\t}\n\treturn false\n}\n\nfunc TestFilterWithContext(t *testing.T) {\n\ttype CtxKey struct {\n\t\tKey string\n\t}\n\tctxKey := CtxKey{Key: \"context\"}\n\tctxValue := \"filter test value\"\n\n\tv1 := func() Valuer {\n\t\treturn func(ctx context.Context) any {\n\t\t\treturn ctx.Value(ctxKey)\n\t\t}\n\t}\n\n\tinfo := &bytes.Buffer{}\n\n\tlogger := With(NewStdLogger(info), \"request_id\", v1())\n\tfilter := NewFilter(logger, FilterLevel(LevelError))\n\n\tctx := context.WithValue(context.Background(), ctxKey, ctxValue)\n\n\t_ = WithContext(ctx, filter).Log(LevelInfo, \"kind\", \"test\")\n\n\tif info.String() != \"\" {\n\t\tt.Error(\"filter is not working\")\n\t\treturn\n\t}\n\n\t_ = WithContext(ctx, filter).Log(LevelError, \"kind\", \"test\")\n\tif !strings.Contains(info.String(), ctxValue) {\n\t\tt.Error(\"don't read ctx value\")\n\t}\n}\n\ntype traceIDKey struct{}\n\nfunc setTraceID(ctx context.Context, tid string) context.Context {\n\treturn context.WithValue(ctx, traceIDKey{}, tid)\n}\n\nfunc traceIDValuer() Valuer {\n\treturn func(ctx context.Context) any {\n\t\tif ctx == nil {\n\t\t\treturn \"\"\n\t\t}\n\t\tif tid := ctx.Value(traceIDKey{}); tid != nil {\n\t\t\treturn tid\n\t\t}\n\t\treturn \"\"\n\t}\n}\n\nfunc TestFilterWithContextConcurrent(t *testing.T) {\n\tvar buf bytes.Buffer\n\tpctx := context.Background()\n\tl := NewFilter(\n\t\tWith(NewStdLogger(&buf), \"trace-id\", traceIDValuer()),\n\t\tFilterLevel(LevelInfo),\n\t)\n\n\tvar wg sync.WaitGroup\n\twg.Add(1)\n\tgo func() {\n\t\tdefer wg.Done()\n\t\ttime.Sleep(time.Second)\n\t\tNewHelper(l).Info(\"done1\")\n\t}()\n\n\twg.Add(1)\n\tgo func() {\n\t\tdefer wg.Done()\n\t\ttid := \"world\"\n\t\tctx := setTraceID(pctx, tid)\n\t\tNewHelper((WithContext(ctx, l))).Info(\"done2\")\n\t}()\n\n\twg.Wait()\n\texpected := \"INFO trace-id=world msg=done2\\nINFO trace-id= msg=done1\\n\"\n\tif got := buf.String(); got != expected {\n\t\tt.Errorf(\"got: %#v\", got)\n\t}\n}\n"
  },
  {
    "path": "log/global.go",
    "content": "package log\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"os\"\n\t\"sync\"\n)\n\n// globalLogger is designed as a global logger in current process.\nvar global = &loggerAppliance{}\n\n// loggerAppliance is the proxy of `Logger` to\n// make logger change will affect all sub-logger.\ntype loggerAppliance struct {\n\tlock sync.RWMutex\n\tLogger\n}\n\nfunc init() {\n\tglobal.SetLogger(DefaultLogger)\n}\n\nfunc (a *loggerAppliance) SetLogger(in Logger) {\n\ta.lock.Lock()\n\tdefer a.lock.Unlock()\n\ta.Logger = in\n}\n\n// SetLogger should be called before any other log call.\n// And it is NOT THREAD SAFE.\nfunc SetLogger(logger Logger) {\n\tglobal.SetLogger(logger)\n}\n\n// GetLogger returns global logger appliance as logger in current process.\nfunc GetLogger() Logger {\n\tglobal.lock.RLock()\n\tdefer global.lock.RUnlock()\n\treturn global.Logger\n}\n\n// Log Print log by level and keyvals.\nfunc Log(level Level, keyvals ...any) {\n\t_ = global.Log(level, keyvals...)\n}\n\n// Context with context logger.\nfunc Context(ctx context.Context) *Helper {\n\treturn NewHelper(WithContext(ctx, global.Logger))\n}\n\n// Debug logs a message at debug level.\nfunc Debug(a ...any) {\n\t_ = global.Log(LevelDebug, DefaultMessageKey, fmt.Sprint(a...))\n}\n\n// Debugf logs a message at debug level.\nfunc Debugf(format string, a ...any) {\n\t_ = global.Log(LevelDebug, DefaultMessageKey, fmt.Sprintf(format, a...))\n}\n\n// Debugw logs a message at debug level.\nfunc Debugw(keyvals ...any) {\n\t_ = global.Log(LevelDebug, keyvals...)\n}\n\n// Info logs a message at info level.\nfunc Info(a ...any) {\n\t_ = global.Log(LevelInfo, DefaultMessageKey, fmt.Sprint(a...))\n}\n\n// Infof logs a message at info level.\nfunc Infof(format string, a ...any) {\n\t_ = global.Log(LevelInfo, DefaultMessageKey, fmt.Sprintf(format, a...))\n}\n\n// Infow logs a message at info level.\nfunc Infow(keyvals ...any) {\n\t_ = global.Log(LevelInfo, keyvals...)\n}\n\n// Warn logs a message at warn level.\nfunc Warn(a ...any) {\n\t_ = global.Log(LevelWarn, DefaultMessageKey, fmt.Sprint(a...))\n}\n\n// Warnf logs a message at warnf level.\nfunc Warnf(format string, a ...any) {\n\t_ = global.Log(LevelWarn, DefaultMessageKey, fmt.Sprintf(format, a...))\n}\n\n// Warnw logs a message at warnf level.\nfunc Warnw(keyvals ...any) {\n\t_ = global.Log(LevelWarn, keyvals...)\n}\n\n// Error logs a message at error level.\nfunc Error(a ...any) {\n\t_ = global.Log(LevelError, DefaultMessageKey, fmt.Sprint(a...))\n}\n\n// Errorf logs a message at error level.\nfunc Errorf(format string, a ...any) {\n\t_ = global.Log(LevelError, DefaultMessageKey, fmt.Sprintf(format, a...))\n}\n\n// Errorw logs a message at error level.\nfunc Errorw(keyvals ...any) {\n\t_ = global.Log(LevelError, keyvals...)\n}\n\n// Fatal logs a message at fatal level.\nfunc Fatal(a ...any) {\n\t_ = global.Log(LevelFatal, DefaultMessageKey, fmt.Sprint(a...))\n\tos.Exit(1)\n}\n\n// Fatalf logs a message at fatal level.\nfunc Fatalf(format string, a ...any) {\n\t_ = global.Log(LevelFatal, DefaultMessageKey, fmt.Sprintf(format, a...))\n\tos.Exit(1)\n}\n\n// Fatalw logs a message at fatal level.\nfunc Fatalw(keyvals ...any) {\n\t_ = global.Log(LevelFatal, keyvals...)\n\tos.Exit(1)\n}\n"
  },
  {
    "path": "log/global_test.go",
    "content": "package log\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"fmt\"\n\t\"os\"\n\t\"strings\"\n\t\"testing\"\n)\n\nfunc TestGlobalLog(t *testing.T) {\n\tbuffer := &bytes.Buffer{}\n\tlogger := NewStdLogger(buffer)\n\tSetLogger(logger)\n\n\tif global.Logger != logger {\n\t\tt.Error(\"GetLogger() is not equal to logger\")\n\t}\n\n\ttestCases := []struct {\n\t\tlevel   Level\n\t\tcontent []any\n\t}{\n\t\t{\n\t\t\tLevelDebug,\n\t\t\t[]any{\"test debug\"},\n\t\t},\n\t\t{\n\t\t\tLevelInfo,\n\t\t\t[]any{\"test info\"},\n\t\t},\n\t\t{\n\t\t\tLevelInfo,\n\t\t\t[]any{\"test %s\", \"info\"},\n\t\t},\n\t\t{\n\t\t\tLevelWarn,\n\t\t\t[]any{\"test warn\"},\n\t\t},\n\t\t{\n\t\t\tLevelError,\n\t\t\t[]any{\"test error\"},\n\t\t},\n\t\t{\n\t\t\tLevelError,\n\t\t\t[]any{\"test %s\", \"error\"},\n\t\t},\n\t}\n\n\tvar expected []string\n\tfor _, tc := range testCases {\n\t\tmsg := fmt.Sprintf(tc.content[0].(string), tc.content[1:]...)\n\t\tswitch tc.level {\n\t\tcase LevelDebug:\n\t\t\tDebug(msg)\n\t\t\texpected = append(expected, fmt.Sprintf(\"%s msg=%s\", \"DEBUG\", msg))\n\t\t\tDebugf(tc.content[0].(string), tc.content[1:]...)\n\t\t\texpected = append(expected, fmt.Sprintf(\"%s msg=%s\", \"DEBUG\", msg))\n\t\t\tDebugw(\"log\", msg)\n\t\t\texpected = append(expected, fmt.Sprintf(\"%s log=%s\", \"DEBUG\", msg))\n\t\tcase LevelInfo:\n\t\t\tInfo(msg)\n\t\t\texpected = append(expected, fmt.Sprintf(\"%s msg=%s\", \"INFO\", msg))\n\t\t\tInfof(tc.content[0].(string), tc.content[1:]...)\n\t\t\texpected = append(expected, fmt.Sprintf(\"%s msg=%s\", \"INFO\", msg))\n\t\t\tInfow(\"log\", msg)\n\t\t\texpected = append(expected, fmt.Sprintf(\"%s log=%s\", \"INFO\", msg))\n\t\tcase LevelWarn:\n\t\t\tWarn(msg)\n\t\t\texpected = append(expected, fmt.Sprintf(\"%s msg=%s\", \"WARN\", msg))\n\t\t\tWarnf(tc.content[0].(string), tc.content[1:]...)\n\t\t\texpected = append(expected, fmt.Sprintf(\"%s msg=%s\", \"WARN\", msg))\n\t\t\tWarnw(\"log\", msg)\n\t\t\texpected = append(expected, fmt.Sprintf(\"%s log=%s\", \"WARN\", msg))\n\t\tcase LevelError:\n\t\t\tError(msg)\n\t\t\texpected = append(expected, fmt.Sprintf(\"%s msg=%s\", \"ERROR\", msg))\n\t\t\tErrorf(tc.content[0].(string), tc.content[1:]...)\n\t\t\texpected = append(expected, fmt.Sprintf(\"%s msg=%s\", \"ERROR\", msg))\n\t\t\tErrorw(\"log\", msg)\n\t\t\texpected = append(expected, fmt.Sprintf(\"%s log=%s\", \"ERROR\", msg))\n\t\t}\n\t}\n\tLog(LevelInfo, DefaultMessageKey, \"test log\")\n\texpected = append(expected, fmt.Sprintf(\"%s msg=%s\", \"INFO\", \"test log\"))\n\n\texpected = append(expected, \"\")\n\n\tt.Logf(\"Content: %s\", buffer.String())\n\tif buffer.String() != strings.Join(expected, \"\\n\") {\n\t\tt.Errorf(\"Expected: %s, got: %s\", strings.Join(expected, \"\\n\"), buffer.String())\n\t}\n}\n\nfunc TestGlobalLogUpdate(t *testing.T) {\n\tl := &loggerAppliance{}\n\tl.SetLogger(NewStdLogger(os.Stdout))\n\tLOG := NewHelper(l)\n\tLOG.Info(\"Log to stdout\")\n\n\tbuffer := &bytes.Buffer{}\n\tl.SetLogger(NewStdLogger(buffer))\n\tLOG.Info(\"Log to buffer\")\n\n\texpected := \"INFO msg=Log to buffer\\n\"\n\tif buffer.String() != expected {\n\t\tt.Errorf(\"Expected: %s, got: %s\", expected, buffer.String())\n\t}\n}\n\nfunc TestGlobalContext(t *testing.T) {\n\tbuffer := &bytes.Buffer{}\n\tSetLogger(NewStdLogger(buffer))\n\tContext(context.Background()).Infof(\"111\")\n\tif buffer.String() != \"INFO msg=111\\n\" {\n\t\tt.Errorf(\"Expected:%s, got:%s\", \"INFO msg=111\", buffer.String())\n\t}\n}\n\nfunc TestContextWithGlobalLog(t *testing.T) {\n\tbuffer := &bytes.Buffer{}\n\n\ttype traceKey struct{}\n\t// set \"trace-id\" Valuer\n\tnewLogger := With(NewStdLogger(buffer), \"trace-id\", Valuer(func(ctx context.Context) any {\n\t\treturn ctx.Value(traceKey{})\n\t}))\n\n\tSetLogger(newLogger)\n\n\t// add value to ctx\n\tctx := context.WithValue(context.Background(), traceKey{}, \"test-trace-id\")\n\n\t_ = WithContext(ctx, GetLogger()).Log(LevelInfo)\n\tif buffer.String() != \"INFO trace-id=test-trace-id\\n\" {\n\t\tt.Errorf(\"Expected:%s, got:%s\", \"INFO trace-id=test-trace-id\", buffer.String())\n\t}\n}\n"
  },
  {
    "path": "log/helper.go",
    "content": "package log\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"os\"\n)\n\n// DefaultMessageKey default message key.\nvar DefaultMessageKey = \"msg\"\n\n// Option is Helper option.\ntype Option func(*Helper)\n\n// Helper is a logger helper.\ntype Helper struct {\n\tlogger  Logger\n\tmsgKey  string\n\tsprint  func(...any) string\n\tsprintf func(format string, a ...any) string\n}\n\n// WithMessageKey with message key.\nfunc WithMessageKey(k string) Option {\n\treturn func(opts *Helper) {\n\t\topts.msgKey = k\n\t}\n}\n\n// WithSprint with sprint\nfunc WithSprint(sprint func(...any) string) Option {\n\treturn func(opts *Helper) {\n\t\topts.sprint = sprint\n\t}\n}\n\n// WithSprintf with sprintf\nfunc WithSprintf(sprintf func(format string, a ...any) string) Option {\n\treturn func(opts *Helper) {\n\t\topts.sprintf = sprintf\n\t}\n}\n\n// NewHelper new a logger helper.\nfunc NewHelper(logger Logger, opts ...Option) *Helper {\n\toptions := &Helper{\n\t\tmsgKey:  DefaultMessageKey, // default message key\n\t\tlogger:  logger,\n\t\tsprint:  fmt.Sprint,\n\t\tsprintf: fmt.Sprintf,\n\t}\n\tfor _, o := range opts {\n\t\to(options)\n\t}\n\treturn options\n}\n\n// WithContext returns a shallow copy of h with its context changed\n// to ctx. The provided ctx must be non-nil.\nfunc (h *Helper) WithContext(ctx context.Context) *Helper {\n\treturn &Helper{\n\t\tmsgKey:  h.msgKey,\n\t\tlogger:  WithContext(ctx, h.logger),\n\t\tsprint:  h.sprint,\n\t\tsprintf: h.sprintf,\n\t}\n}\n\n// Enabled returns true if the given level above this level.\n// It delegates to the underlying *Filter.\nfunc (h *Helper) Enabled(level Level) bool {\n\tif l, ok := h.logger.(*Filter); ok {\n\t\treturn level >= l.level\n\t}\n\treturn true\n}\n\n// Logger returns logger in the helper.\nfunc (h *Helper) Logger() Logger {\n\treturn h.logger\n}\n\n// Log Print log by level and keyvals.\nfunc (h *Helper) Log(level Level, keyvals ...any) {\n\t_ = h.logger.Log(level, keyvals...)\n}\n\n// Debug logs a message at debug level.\nfunc (h *Helper) Debug(a ...any) {\n\tif !h.Enabled(LevelDebug) {\n\t\treturn\n\t}\n\t_ = h.logger.Log(LevelDebug, h.msgKey, h.sprint(a...))\n}\n\n// Debugf logs a message at debug level.\nfunc (h *Helper) Debugf(format string, a ...any) {\n\tif !h.Enabled(LevelDebug) {\n\t\treturn\n\t}\n\t_ = h.logger.Log(LevelDebug, h.msgKey, h.sprintf(format, a...))\n}\n\n// Debugw logs a message at debug level.\nfunc (h *Helper) Debugw(keyvals ...any) {\n\t_ = h.logger.Log(LevelDebug, keyvals...)\n}\n\n// Info logs a message at info level.\nfunc (h *Helper) Info(a ...any) {\n\tif !h.Enabled(LevelInfo) {\n\t\treturn\n\t}\n\t_ = h.logger.Log(LevelInfo, h.msgKey, h.sprint(a...))\n}\n\n// Infof logs a message at info level.\nfunc (h *Helper) Infof(format string, a ...any) {\n\tif !h.Enabled(LevelInfo) {\n\t\treturn\n\t}\n\t_ = h.logger.Log(LevelInfo, h.msgKey, h.sprintf(format, a...))\n}\n\n// Infow logs a message at info level.\nfunc (h *Helper) Infow(keyvals ...any) {\n\t_ = h.logger.Log(LevelInfo, keyvals...)\n}\n\n// Warn logs a message at warn level.\nfunc (h *Helper) Warn(a ...any) {\n\tif !h.Enabled(LevelWarn) {\n\t\treturn\n\t}\n\t_ = h.logger.Log(LevelWarn, h.msgKey, h.sprint(a...))\n}\n\n// Warnf logs a message at warnf level.\nfunc (h *Helper) Warnf(format string, a ...any) {\n\tif !h.Enabled(LevelWarn) {\n\t\treturn\n\t}\n\t_ = h.logger.Log(LevelWarn, h.msgKey, h.sprintf(format, a...))\n}\n\n// Warnw logs a message at warnf level.\nfunc (h *Helper) Warnw(keyvals ...any) {\n\t_ = h.logger.Log(LevelWarn, keyvals...)\n}\n\n// Error logs a message at error level.\nfunc (h *Helper) Error(a ...any) {\n\tif !h.Enabled(LevelError) {\n\t\treturn\n\t}\n\t_ = h.logger.Log(LevelError, h.msgKey, h.sprint(a...))\n}\n\n// Errorf logs a message at error level.\nfunc (h *Helper) Errorf(format string, a ...any) {\n\tif !h.Enabled(LevelError) {\n\t\treturn\n\t}\n\t_ = h.logger.Log(LevelError, h.msgKey, h.sprintf(format, a...))\n}\n\n// Errorw logs a message at error level.\nfunc (h *Helper) Errorw(keyvals ...any) {\n\t_ = h.logger.Log(LevelError, keyvals...)\n}\n\n// Fatal logs a message at fatal level.\nfunc (h *Helper) Fatal(a ...any) {\n\t_ = h.logger.Log(LevelFatal, h.msgKey, h.sprint(a...))\n\tos.Exit(1)\n}\n\n// Fatalf logs a message at fatal level.\nfunc (h *Helper) Fatalf(format string, a ...any) {\n\t_ = h.logger.Log(LevelFatal, h.msgKey, h.sprintf(format, a...))\n\tos.Exit(1)\n}\n\n// Fatalw logs a message at fatal level.\nfunc (h *Helper) Fatalw(keyvals ...any) {\n\t_ = h.logger.Log(LevelFatal, keyvals...)\n\tos.Exit(1)\n}\n"
  },
  {
    "path": "log/helper_test.go",
    "content": "package log\n\nimport (\n\t\"context\"\n\t\"io\"\n\t\"os\"\n\t\"testing\"\n)\n\nfunc TestHelper(_ *testing.T) {\n\tlogger := With(\n\t\tDefaultLogger,\n\t\t\"ts\", DefaultTimestamp,\n\t\t\"caller\", DefaultCaller,\n\t\t\"module\", \"test\",\n\t)\n\tlog := NewHelper(logger)\n\n\tlog.Log(LevelDebug, \"msg\", \"test debug\")\n\tlog.Debug(\"test debug\")\n\tlog.Debugf(\"test %s\", \"debug\")\n\tlog.Debugw(\"log\", \"test debug\")\n\n\tlog.Warn(\"test warn\")\n\tlog.Warnf(\"test %s\", \"warn\")\n\tlog.Warnw(\"log\", \"test warn\")\n\n\tsubLogger := With(log.Logger(),\n\t\t\"module\", \"sub\",\n\t)\n\tsubLog := NewHelper(subLogger)\n\tsubLog.Infof(\"sub logger test with level %s\", \"info\")\n}\n\nfunc TestHelperWithMsgKey(_ *testing.T) {\n\tlogger := With(DefaultLogger, \"ts\", DefaultTimestamp, \"caller\", DefaultCaller)\n\tlog := NewHelper(logger, WithMessageKey(\"message\"))\n\tlog.Debugf(\"test %s\", \"debug\")\n\tlog.Debugw(\"log\", \"test debug\")\n}\n\nfunc TestHelperLevel(_ *testing.T) {\n\tlog := NewHelper(DefaultLogger)\n\tlog.Debug(\"test debug\")\n\tlog.Info(\"test info\")\n\tlog.Infof(\"test %s\", \"info\")\n\tlog.Warn(\"test warn\")\n\tlog.Error(\"test error\")\n\tlog.Errorf(\"test %s\", \"error\")\n\tlog.Errorw(\"log\", \"test error\")\n}\n\nfunc BenchmarkHelperPrint(b *testing.B) {\n\tlog := NewHelper(NewStdLogger(io.Discard))\n\tfor i := 0; i < b.N; i++ {\n\t\tlog.Debug(\"test\")\n\t}\n}\n\nfunc BenchmarkHelperPrintFilterLevel(b *testing.B) {\n\tlog := NewHelper(NewFilter(NewStdLogger(io.Discard), FilterLevel(LevelDebug)))\n\tfor i := 0; i < b.N; i++ {\n\t\tlog.Debug(\"test\")\n\t}\n}\n\nfunc BenchmarkHelperPrintf(b *testing.B) {\n\tlog := NewHelper(NewStdLogger(io.Discard))\n\tfor i := 0; i < b.N; i++ {\n\t\tlog.Debugf(\"%s\", \"test\")\n\t}\n}\n\nfunc BenchmarkHelperPrintfFilterLevel(b *testing.B) {\n\tlog := NewHelper(NewFilter(NewStdLogger(io.Discard), FilterLevel(LevelInfo)))\n\tfor i := 0; i < b.N; i++ {\n\t\tlog.Debugf(\"%s\", \"test\")\n\t}\n}\n\nfunc BenchmarkHelperPrintw(b *testing.B) {\n\tlog := NewHelper(NewStdLogger(io.Discard))\n\tfor i := 0; i < b.N; i++ {\n\t\tlog.Debugw(\"key\", \"value\")\n\t}\n}\n\ntype traceKey struct{}\n\nfunc TestContext(_ *testing.T) {\n\tlogger := With(NewStdLogger(os.Stdout),\n\t\t\"trace\", Trace(),\n\t)\n\tlog := NewHelper(logger)\n\tctx := context.WithValue(context.Background(), traceKey{}, \"2233\")\n\tlog.WithContext(ctx).Info(\"got trace!\")\n}\n\nfunc Trace() Valuer {\n\treturn func(ctx context.Context) any {\n\t\ts, ok := ctx.Value(traceKey{}).(string)\n\t\tif !ok {\n\t\t\treturn nil\n\t\t}\n\t\treturn s\n\t}\n}\n"
  },
  {
    "path": "log/helper_writer.go",
    "content": "package log\n\nimport \"io\"\n\ntype writerWrapper struct {\n\thelper *Helper\n\tlevel  Level\n}\n\ntype WriterOptionFn func(w *writerWrapper)\n\n// WithWriterLevel set writerWrapper level.\nfunc WithWriterLevel(level Level) WriterOptionFn {\n\treturn func(w *writerWrapper) {\n\t\tw.level = level\n\t}\n}\n\n// WithWriteMessageKey set writerWrapper helper message key.\nfunc WithWriteMessageKey(key string) WriterOptionFn {\n\treturn func(w *writerWrapper) {\n\t\tw.helper.msgKey = key\n\t}\n}\n\n// NewWriter return a writer wrapper.\nfunc NewWriter(logger Logger, opts ...WriterOptionFn) io.Writer {\n\tww := &writerWrapper{\n\t\thelper: NewHelper(logger, WithMessageKey(DefaultMessageKey)),\n\t\tlevel:  LevelInfo, // default level\n\t}\n\tfor _, opt := range opts {\n\t\topt(ww)\n\t}\n\treturn ww\n}\n\nfunc (ww *writerWrapper) Write(p []byte) (int, error) {\n\tww.helper.Log(ww.level, ww.helper.msgKey, string(p))\n\treturn 0, nil\n}\n"
  },
  {
    "path": "log/helper_writer_test.go",
    "content": "package log\n\nimport (\n\t\"bytes\"\n\t\"io\"\n\t\"strings\"\n\t\"testing\"\n)\n\nfunc TestWriterWrapper(t *testing.T) {\n\tvar buf bytes.Buffer\n\tlogger := NewStdLogger(&buf)\n\tcontent := \"ThisIsSomeTestLogMessage\"\n\ttestCases := []struct {\n\t\tw                io.Writer\n\t\tacceptLevel      Level\n\t\tacceptMessageKey string\n\t}{\n\t\t{\n\t\t\tw:                NewWriter(logger),\n\t\t\tacceptLevel:      LevelInfo, // default level\n\t\t\tacceptMessageKey: DefaultMessageKey,\n\t\t},\n\t\t{\n\t\t\tw:                NewWriter(logger, WithWriterLevel(LevelDebug)),\n\t\t\tacceptLevel:      LevelDebug,\n\t\t\tacceptMessageKey: DefaultMessageKey,\n\t\t},\n\t\t{\n\t\t\tw:                NewWriter(logger, WithWriteMessageKey(\"XxXxX\")),\n\t\t\tacceptLevel:      LevelInfo, // default level\n\t\t\tacceptMessageKey: \"XxXxX\",\n\t\t},\n\t\t{\n\t\t\tw:                NewWriter(logger, WithWriterLevel(LevelError), WithWriteMessageKey(\"XxXxX\")),\n\t\t\tacceptLevel:      LevelError,\n\t\t\tacceptMessageKey: \"XxXxX\",\n\t\t},\n\t}\n\tfor _, tc := range testCases {\n\t\t_, err := tc.w.Write([]byte(content))\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"unexpected error: %v\", err)\n\t\t}\n\t\tif !strings.Contains(buf.String(), tc.acceptLevel.String()) {\n\t\t\tt.Errorf(\"expected level: %s, got: %s\", tc.acceptLevel, buf.String())\n\t\t}\n\t\tif !strings.Contains(buf.String(), tc.acceptMessageKey) {\n\t\t\tt.Errorf(\"expected message key: %s, got: %s\", tc.acceptMessageKey, buf.String())\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "log/level.go",
    "content": "package log\n\nimport \"strings\"\n\n// Level is a logger level.\ntype Level int8\n\n// LevelKey is logger level key.\nconst LevelKey = \"level\"\n\nconst (\n\t// LevelDebug is logger debug level.\n\tLevelDebug Level = iota - 1\n\t// LevelInfo is logger info level.\n\tLevelInfo\n\t// LevelWarn is logger warn level.\n\tLevelWarn\n\t// LevelError is logger error level.\n\tLevelError\n\t// LevelFatal is logger fatal level\n\tLevelFatal\n)\n\nfunc (l Level) Key() string {\n\treturn LevelKey\n}\n\nfunc (l Level) String() string {\n\tswitch l {\n\tcase LevelDebug:\n\t\treturn \"DEBUG\"\n\tcase LevelInfo:\n\t\treturn \"INFO\"\n\tcase LevelWarn:\n\t\treturn \"WARN\"\n\tcase LevelError:\n\t\treturn \"ERROR\"\n\tcase LevelFatal:\n\t\treturn \"FATAL\"\n\tdefault:\n\t\treturn \"\"\n\t}\n}\n\n// ParseLevel parses a level string into a logger Level value.\nfunc ParseLevel(s string) Level {\n\tswitch strings.ToUpper(s) {\n\tcase \"DEBUG\":\n\t\treturn LevelDebug\n\tcase \"INFO\":\n\t\treturn LevelInfo\n\tcase \"WARN\":\n\t\treturn LevelWarn\n\tcase \"ERROR\":\n\t\treturn LevelError\n\tcase \"FATAL\":\n\t\treturn LevelFatal\n\t}\n\treturn LevelInfo\n}\n"
  },
  {
    "path": "log/level_test.go",
    "content": "package log\n\nimport \"testing\"\n\nfunc TestLevel_Key(t *testing.T) {\n\tif LevelInfo.Key() != LevelKey {\n\t\tt.Errorf(\"want: %s, got: %s\", LevelKey, LevelInfo.Key())\n\t}\n}\n\nfunc TestLevel_String(t *testing.T) {\n\ttests := []struct {\n\t\tname string\n\t\tl    Level\n\t\twant string\n\t}{\n\t\t{\n\t\t\tname: \"DEBUG\",\n\t\t\tl:    LevelDebug,\n\t\t\twant: \"DEBUG\",\n\t\t},\n\t\t{\n\t\t\tname: \"INFO\",\n\t\t\tl:    LevelInfo,\n\t\t\twant: \"INFO\",\n\t\t},\n\t\t{\n\t\t\tname: \"WARN\",\n\t\t\tl:    LevelWarn,\n\t\t\twant: \"WARN\",\n\t\t},\n\t\t{\n\t\t\tname: \"ERROR\",\n\t\t\tl:    LevelError,\n\t\t\twant: \"ERROR\",\n\t\t},\n\t\t{\n\t\t\tname: \"FATAL\",\n\t\t\tl:    LevelFatal,\n\t\t\twant: \"FATAL\",\n\t\t},\n\t\t{\n\t\t\tname: \"other\",\n\t\t\tl:    10,\n\t\t\twant: \"\",\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tif got := tt.l.String(); got != tt.want {\n\t\t\t\tt.Errorf(\"String() = %v, want %v\", got, tt.want)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestParseLevel(t *testing.T) {\n\ttests := []struct {\n\t\tname string\n\t\ts    string\n\t\twant Level\n\t}{\n\t\t{\n\t\t\tname: \"DEBUG\",\n\t\t\twant: LevelDebug,\n\t\t\ts:    \"DEBUG\",\n\t\t},\n\t\t{\n\t\t\tname: \"INFO\",\n\t\t\twant: LevelInfo,\n\t\t\ts:    \"INFO\",\n\t\t},\n\t\t{\n\t\t\tname: \"WARN\",\n\t\t\twant: LevelWarn,\n\t\t\ts:    \"WARN\",\n\t\t},\n\t\t{\n\t\t\tname: \"ERROR\",\n\t\t\twant: LevelError,\n\t\t\ts:    \"ERROR\",\n\t\t},\n\t\t{\n\t\t\tname: \"FATAL\",\n\t\t\twant: LevelFatal,\n\t\t\ts:    \"FATAL\",\n\t\t},\n\t\t{\n\t\t\tname: \"other\",\n\t\t\twant: LevelInfo,\n\t\t\ts:    \"other\",\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tif got := ParseLevel(tt.s); got != tt.want {\n\t\t\t\tt.Errorf(\"ParseLevel() = %v, want %v\", got, tt.want)\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "log/log.go",
    "content": "package log\n\nimport (\n\t\"context\"\n\t\"log\"\n)\n\n// DefaultLogger is default logger.\nvar DefaultLogger = NewStdLogger(log.Writer())\n\n// Logger is a logger interface.\ntype Logger interface {\n\tLog(level Level, keyvals ...any) error\n}\n\ntype logger struct {\n\tlogger    Logger\n\tprefix    []any\n\thasValuer bool\n\tctx       context.Context\n}\n\nfunc (c *logger) Log(level Level, keyvals ...any) error {\n\tkvs := make([]any, 0, len(c.prefix)+len(keyvals))\n\tkvs = append(kvs, c.prefix...)\n\tif c.hasValuer {\n\t\tbindValues(c.ctx, kvs)\n\t}\n\tkvs = append(kvs, keyvals...)\n\treturn c.logger.Log(level, kvs...)\n}\n\n// With with logger fields.\nfunc With(l Logger, kv ...any) Logger {\n\tc, ok := l.(*logger)\n\tif !ok {\n\t\treturn &logger{logger: l, prefix: kv, hasValuer: containsValuer(kv), ctx: context.Background()}\n\t}\n\tkvs := make([]any, 0, len(c.prefix)+len(kv))\n\tkvs = append(kvs, c.prefix...)\n\tkvs = append(kvs, kv...)\n\treturn &logger{\n\t\tlogger:    c.logger,\n\t\tprefix:    kvs,\n\t\thasValuer: containsValuer(kvs),\n\t\tctx:       c.ctx,\n\t}\n}\n\n// WithContext returns a shallow copy of l with its context changed\n// to ctx. The provided ctx must be non-nil.\nfunc WithContext(ctx context.Context, l Logger) Logger {\n\tswitch v := l.(type) {\n\tdefault:\n\t\treturn &logger{logger: l, ctx: ctx}\n\tcase *logger:\n\t\tlv := *v\n\t\tlv.ctx = ctx\n\t\treturn &lv\n\tcase *Filter:\n\t\tfv := *v\n\t\tfv.logger = WithContext(ctx, fv.logger)\n\t\treturn &fv\n\t}\n}\n"
  },
  {
    "path": "log/log_test.go",
    "content": "package log\n\nimport (\n\t\"testing\"\n)\n\nfunc TestInfo(_ *testing.T) {\n\tlogger := DefaultLogger\n\tlogger = With(logger, \"ts\", DefaultTimestamp)\n\tlogger = With(logger, \"caller\", DefaultCaller)\n\t_ = logger.Log(LevelInfo, \"key1\", \"value1\")\n}\n"
  },
  {
    "path": "log/std.go",
    "content": "package log\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"io\"\n\t\"sync\"\n)\n\nvar _ Logger = (*stdLogger)(nil)\n\n// stdLogger corresponds to the standard library's [log.Logger] and provides\n// similar capabilities. It also can be used concurrently by multiple goroutines.\ntype stdLogger struct {\n\tw         io.Writer\n\tisDiscard bool\n\tmu        sync.Mutex\n\tpool      *sync.Pool\n}\n\n// NewStdLogger new a logger with writer.\nfunc NewStdLogger(w io.Writer) Logger {\n\treturn &stdLogger{\n\t\tw:         w,\n\t\tisDiscard: w == io.Discard,\n\t\tpool: &sync.Pool{\n\t\t\tNew: func() any {\n\t\t\t\treturn new(bytes.Buffer)\n\t\t\t},\n\t\t},\n\t}\n}\n\n// Log print the kv pairs log.\nfunc (l *stdLogger) Log(level Level, keyvals ...any) error {\n\tif l.isDiscard || len(keyvals) == 0 {\n\t\treturn nil\n\t}\n\tif (len(keyvals) & 1) == 1 {\n\t\tkeyvals = append(keyvals, \"KEYVALS UNPAIRED\")\n\t}\n\n\tbuf := l.pool.Get().(*bytes.Buffer)\n\tdefer l.pool.Put(buf)\n\n\tbuf.WriteString(level.String())\n\tfor i := 0; i < len(keyvals); i += 2 {\n\t\t_, _ = fmt.Fprintf(buf, \" %s=%v\", keyvals[i], keyvals[i+1])\n\t}\n\tbuf.WriteByte('\\n')\n\tdefer buf.Reset()\n\n\tl.mu.Lock()\n\tdefer l.mu.Unlock()\n\t_, err := l.w.Write(buf.Bytes())\n\treturn err\n}\n\nfunc (l *stdLogger) Close() error {\n\treturn nil\n}\n"
  },
  {
    "path": "log/std_test.go",
    "content": "package log\n\nimport (\n\t\"bytes\"\n\t\"testing\"\n\n\t\"golang.org/x/sync/errgroup\"\n)\n\nfunc TestStdLogger(_ *testing.T) {\n\tlogger := DefaultLogger\n\tlogger = With(logger, \"caller\", DefaultCaller, \"ts\", DefaultTimestamp)\n\n\t_ = logger.Log(LevelInfo, \"msg\", \"test debug\")\n\t_ = logger.Log(LevelInfo, \"msg\", \"test info\")\n\t_ = logger.Log(LevelInfo, \"msg\", \"test warn\")\n\t_ = logger.Log(LevelInfo, \"msg\", \"test error\")\n\t_ = logger.Log(LevelDebug, \"singular\")\n\n\tlogger2 := DefaultLogger\n\t_ = logger2.Log(LevelDebug)\n}\n\nfunc TestStdLogger_Log(t *testing.T) {\n\tvar b bytes.Buffer\n\tlogger := NewStdLogger(&b)\n\n\tvar eg errgroup.Group\n\teg.Go(func() error { return logger.Log(LevelInfo, \"msg\", \"a\", \"k\", \"v\") })\n\teg.Go(func() error { return logger.Log(LevelInfo, \"msg\", \"a\", \"k\", \"v\") })\n\n\terr := eg.Wait()\n\tif err != nil {\n\t\tt.Fatalf(\"log error: %v\", err)\n\t}\n\n\tif s := b.String(); s != \"INFO msg=a k=v\\nINFO msg=a k=v\\n\" {\n\t\tt.Fatalf(\"log not match: %q\", s)\n\t}\n}\n"
  },
  {
    "path": "log/value.go",
    "content": "package log\n\nimport (\n\t\"context\"\n\t\"runtime\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n)\n\nvar (\n\t// DefaultCaller is a Valuer that returns the file and line.\n\tDefaultCaller = Caller(4)\n\n\t// DefaultTimestamp is a Valuer that returns the current wallclock time.\n\tDefaultTimestamp = Timestamp(time.RFC3339)\n)\n\n// Valuer is returns a log value.\ntype Valuer func(ctx context.Context) any\n\n// Value return the function value.\nfunc Value(ctx context.Context, v any) any {\n\tif v, ok := v.(Valuer); ok {\n\t\treturn v(ctx)\n\t}\n\treturn v\n}\n\n// Caller returns a Valuer that returns a pkg/file:line description of the caller.\nfunc Caller(depth int) Valuer {\n\treturn func(context.Context) any {\n\t\t_, file, line, _ := runtime.Caller(depth)\n\t\tidx := strings.LastIndexByte(file, '/')\n\t\tif idx == -1 {\n\t\t\treturn file[idx+1:] + \":\" + strconv.Itoa(line)\n\t\t}\n\t\tidx = strings.LastIndexByte(file[:idx], '/')\n\t\treturn file[idx+1:] + \":\" + strconv.Itoa(line)\n\t}\n}\n\n// Timestamp returns a timestamp Valuer with a custom time format.\nfunc Timestamp(layout string) Valuer {\n\treturn func(context.Context) any {\n\t\treturn time.Now().Format(layout)\n\t}\n}\n\nfunc bindValues(ctx context.Context, keyvals []any) {\n\tfor i := 1; i < len(keyvals); i += 2 {\n\t\tif v, ok := keyvals[i].(Valuer); ok {\n\t\t\tkeyvals[i] = v(ctx)\n\t\t}\n\t}\n}\n\nfunc containsValuer(keyvals []any) bool {\n\tfor i := 1; i < len(keyvals); i += 2 {\n\t\tif _, ok := keyvals[i].(Valuer); ok {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n"
  },
  {
    "path": "log/value_test.go",
    "content": "package log\n\nimport (\n\t\"context\"\n\t\"testing\"\n)\n\nfunc TestValue(t *testing.T) {\n\tlogger := DefaultLogger\n\tlogger = With(logger, \"ts\", DefaultTimestamp, \"caller\", DefaultCaller)\n\t_ = logger.Log(LevelInfo, \"msg\", \"helloworld\")\n\n\tlogger = DefaultLogger\n\tlogger = With(logger)\n\t_ = logger.Log(LevelDebug, \"msg\", \"helloworld\")\n\n\tvar v1 any\n\tgot := Value(context.Background(), v1)\n\tif got != v1 {\n\t\tt.Errorf(\"Value() = %v, want %v\", got, v1)\n\t}\n\tvar v2 Valuer = func(context.Context) any {\n\t\treturn 3\n\t}\n\tgot = Value(context.Background(), v2)\n\tres := got.(int)\n\tif res != 3 {\n\t\tt.Errorf(\"Value() = %v, want %v\", res, 3)\n\t}\n}\n"
  },
  {
    "path": "metadata/metadata.go",
    "content": "package metadata\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"slices\"\n\t\"strings\"\n)\n\n// Metadata is our way of representing request headers internally.\n// They're used at the RPC level and translate back and forth\n// from Transport headers.\ntype Metadata map[string][]string\n\n// New creates an MD from a given key-values map.\nfunc New(mds ...map[string][]string) Metadata {\n\tmd := Metadata{}\n\tfor _, m := range mds {\n\t\tfor k, vList := range m {\n\t\t\tfor _, v := range vList {\n\t\t\t\tmd.Add(k, v)\n\t\t\t}\n\t\t}\n\t}\n\treturn md\n}\n\n// Add adds the key, value pair to the header.\nfunc (m Metadata) Add(key, value string) {\n\tif key == \"\" {\n\t\treturn\n\t}\n\n\tlowerKey := strings.ToLower(key)\n\tm[lowerKey] = append(m[lowerKey], value)\n}\n\n// Get returns the value associated with the passed key.\nfunc (m Metadata) Get(key string) string {\n\tv := m[strings.ToLower(key)]\n\tif len(v) == 0 {\n\t\treturn \"\"\n\t}\n\treturn v[0]\n}\n\n// Set stores the key-value pair.\nfunc (m Metadata) Set(key string, value string) {\n\tif key == \"\" || value == \"\" {\n\t\treturn\n\t}\n\tm[strings.ToLower(key)] = []string{value}\n}\n\n// Range iterate over element in metadata.\nfunc (m Metadata) Range(f func(k string, v []string) bool) {\n\tfor k, v := range m {\n\t\tif !f(k, v) {\n\t\t\tbreak\n\t\t}\n\t}\n}\n\n// Values returns a slice of values associated with the passed key.\nfunc (m Metadata) Values(key string) []string {\n\treturn m[strings.ToLower(key)]\n}\n\n// Clone returns a deep copy of Metadata\nfunc (m Metadata) Clone() Metadata {\n\tmd := make(Metadata, len(m))\n\tfor k, v := range m {\n\t\tmd[k] = slices.Clone(v)\n\t}\n\treturn md\n}\n\ntype serverMetadataKey struct{}\n\n// NewServerContext creates a new context with client md attached.\nfunc NewServerContext(ctx context.Context, md Metadata) context.Context {\n\treturn context.WithValue(ctx, serverMetadataKey{}, md)\n}\n\n// FromServerContext returns the server metadata in ctx if it exists.\nfunc FromServerContext(ctx context.Context) (Metadata, bool) {\n\tmd, ok := ctx.Value(serverMetadataKey{}).(Metadata)\n\treturn md, ok\n}\n\ntype clientMetadataKey struct{}\n\n// NewClientContext creates a new context with client md attached.\nfunc NewClientContext(ctx context.Context, md Metadata) context.Context {\n\treturn context.WithValue(ctx, clientMetadataKey{}, md)\n}\n\n// FromClientContext returns the client metadata in ctx if it exists.\nfunc FromClientContext(ctx context.Context) (Metadata, bool) {\n\tmd, ok := ctx.Value(clientMetadataKey{}).(Metadata)\n\treturn md, ok\n}\n\n// AppendToClientContext returns a new context with the provided kv merged\n// with any existing metadata in the context.\nfunc AppendToClientContext(ctx context.Context, kv ...string) context.Context {\n\tif len(kv)%2 == 1 {\n\t\tpanic(fmt.Sprintf(\"metadata: AppendToClientContext got an odd number of input pairs for metadata: %d\", len(kv)))\n\t}\n\tmd, _ := FromClientContext(ctx)\n\tmd = md.Clone()\n\tfor i := 0; i < len(kv); i += 2 {\n\t\tmd.Set(kv[i], kv[i+1])\n\t}\n\treturn NewClientContext(ctx, md)\n}\n\n// MergeToClientContext merge new metadata into ctx.\nfunc MergeToClientContext(ctx context.Context, cmd Metadata) context.Context {\n\tmd, _ := FromClientContext(ctx)\n\tmd = md.Clone()\n\tfor k, v := range cmd {\n\t\tmd[k] = v\n\t}\n\treturn NewClientContext(ctx, md)\n}\n"
  },
  {
    "path": "metadata/metadata_test.go",
    "content": "package metadata\n\nimport (\n\t\"context\"\n\t\"reflect\"\n\t\"testing\"\n)\n\nfunc TestNew(t *testing.T) {\n\ttype args struct {\n\t\tmds []map[string][]string\n\t}\n\ttests := []struct {\n\t\tname string\n\t\targs args\n\t\twant Metadata\n\t}{\n\t\t{\n\t\t\tname: \"hello\",\n\t\t\targs: args{[]map[string][]string{{\"hello\": {\"kratos\"}}, {\"hello2\": {\"go-kratos\"}}}},\n\t\t\twant: Metadata{\"hello\": {\"kratos\"}, \"hello2\": {\"go-kratos\"}},\n\t\t},\n\t\t{\n\t\t\tname: \"hi\",\n\t\t\targs: args{[]map[string][]string{{\"hi\": {\"kratos\"}}, {\"hi2\": {\"go-kratos\"}}}},\n\t\t\twant: Metadata{\"hi\": {\"kratos\"}, \"hi2\": {\"go-kratos\"}},\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tif got := New(tt.args.mds...); !reflect.DeepEqual(got, tt.want) {\n\t\t\t\tt.Errorf(\"New() = %v, want %v\", got, tt.want)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestMetadata_Get(t *testing.T) {\n\ttype args struct {\n\t\tkey string\n\t}\n\ttests := []struct {\n\t\tname string\n\t\tm    Metadata\n\t\targs args\n\t\twant string\n\t}{\n\t\t{\n\t\t\tname: \"kratos\",\n\t\t\tm:    Metadata{\"kratos\": {\"value\"}, \"env\": {\"dev\"}},\n\t\t\targs: args{key: \"kratos\"},\n\t\t\twant: \"value\",\n\t\t},\n\t\t{\n\t\t\tname: \"env\",\n\t\t\tm:    Metadata{\"kratos\": {\"value\"}, \"env\": {\"dev\"}},\n\t\t\targs: args{key: \"env\"},\n\t\t\twant: \"dev\",\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tif got := tt.m.Get(tt.args.key); got != tt.want {\n\t\t\t\tt.Errorf(\"Get() = %v, want %v\", got, tt.want)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestMetadata_Values(t *testing.T) {\n\ttype args struct {\n\t\tkey string\n\t}\n\ttests := []struct {\n\t\tname string\n\t\tm    Metadata\n\t\targs args\n\t\twant []string\n\t}{\n\t\t{\n\t\t\tname: \"kratos\",\n\t\t\tm:    Metadata{\"kratos\": {\"value\", \"value2\"}, \"env\": {\"dev\"}},\n\t\t\targs: args{key: \"kratos\"},\n\t\t\twant: []string{\"value\", \"value2\"},\n\t\t},\n\t\t{\n\t\t\tname: \"env\",\n\t\t\tm:    Metadata{\"kratos\": {\"value\", \"value2\"}, \"env\": {\"dev\"}},\n\t\t\targs: args{key: \"env\"},\n\t\t\twant: []string{\"dev\"},\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tif got := tt.m.Values(tt.args.key); !reflect.DeepEqual(got, tt.want) {\n\t\t\t\tt.Errorf(\"Get() = %v, want %v\", got, tt.want)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestMetadata_Set(t *testing.T) {\n\ttype args struct {\n\t\tkey   string\n\t\tvalue string\n\t}\n\ttests := []struct {\n\t\tname string\n\t\tm    Metadata\n\t\targs args\n\t\twant Metadata\n\t}{\n\t\t{\n\t\t\tname: \"kratos\",\n\t\t\tm:    Metadata{},\n\t\t\targs: args{key: \"hello\", value: \"kratos\"},\n\t\t\twant: Metadata{\"hello\": {\"kratos\"}},\n\t\t},\n\t\t{\n\t\t\tname: \"env\",\n\t\t\tm:    Metadata{\"hello\": {\"kratos\"}},\n\t\t\targs: args{key: \"env\", value: \"pro\"},\n\t\t\twant: Metadata{\"hello\": {\"kratos\"}, \"env\": {\"pro\"}},\n\t\t},\n\t\t{\n\t\t\tname: \"empty\",\n\t\t\tm:    Metadata{},\n\t\t\targs: args{key: \"\", value: \"\"},\n\t\t\twant: Metadata{},\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\ttt.m.Set(tt.args.key, tt.args.value)\n\t\t\tif !reflect.DeepEqual(tt.m, tt.want) {\n\t\t\t\tt.Errorf(\"Set() = %v, want %v\", tt.m, tt.want)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestMetadata_Add(t *testing.T) {\n\ttype args struct {\n\t\tkey   string\n\t\tvalue string\n\t}\n\ttests := []struct {\n\t\tname string\n\t\tm    Metadata\n\t\targs args\n\t\twant Metadata\n\t}{\n\t\t{\n\t\t\tname: \"kratos\",\n\t\t\tm:    Metadata{},\n\t\t\targs: args{key: \"hello\", value: \"kratos\"},\n\t\t\twant: Metadata{\"hello\": {\"kratos\"}},\n\t\t},\n\t\t{\n\t\t\tname: \"env\",\n\t\t\tm:    Metadata{\"hello\": {\"kratos\"}},\n\t\t\targs: args{key: \"hello\", value: \"again\"},\n\t\t\twant: Metadata{\"hello\": {\"kratos\", \"again\"}},\n\t\t},\n\t\t{\n\t\t\tname: \"empty\",\n\t\t\tm:    Metadata{},\n\t\t\targs: args{key: \"\", value: \"\"},\n\t\t\twant: Metadata{},\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\ttt.m.Add(tt.args.key, tt.args.value)\n\t\t\tif !reflect.DeepEqual(tt.m, tt.want) {\n\t\t\t\tt.Errorf(\"Set() = %v, want %v\", tt.m, tt.want)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestClientContext(t *testing.T) {\n\ttype args struct {\n\t\tctx context.Context\n\t\tmd  Metadata\n\t}\n\ttests := []struct {\n\t\tname string\n\t\targs args\n\t}{\n\t\t{\n\t\t\tname: \"kratos\",\n\t\t\targs: args{context.Background(), Metadata{\"hello\": {\"kratos\"}, \"kratos\": {\"https://go-kratos.dev\"}}},\n\t\t},\n\t\t{\n\t\t\tname: \"hello\",\n\t\t\targs: args{context.Background(), Metadata{\"hello\": {\"kratos\"}, \"hello2\": {\"https://go-kratos.dev\"}}},\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tctx := NewClientContext(tt.args.ctx, tt.args.md)\n\t\t\tm, ok := FromClientContext(ctx)\n\t\t\tif !ok {\n\t\t\t\tt.Errorf(\"FromClientContext() = %v, want %v\", ok, true)\n\t\t\t}\n\n\t\t\tif !reflect.DeepEqual(m, tt.args.md) {\n\t\t\t\tt.Errorf(\"meta = %v, want %v\", m, tt.args.md)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestServerContext(t *testing.T) {\n\ttype args struct {\n\t\tctx context.Context\n\t\tmd  Metadata\n\t}\n\ttests := []struct {\n\t\tname string\n\t\targs args\n\t}{\n\t\t{\n\t\t\tname: \"kratos\",\n\t\t\targs: args{context.Background(), Metadata{\"hello\": {\"kratos\"}, \"kratos\": {\"https://go-kratos.dev\"}}},\n\t\t},\n\t\t{\n\t\t\tname: \"hello\",\n\t\t\targs: args{context.Background(), Metadata{\"hello\": {\"kratos\"}, \"hello2\": {\"https://go-kratos.dev\"}}},\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tctx := NewServerContext(tt.args.ctx, tt.args.md)\n\t\t\tm, ok := FromServerContext(ctx)\n\t\t\tif !ok {\n\t\t\t\tt.Errorf(\"FromServerContext() = %v, want %v\", ok, true)\n\t\t\t}\n\n\t\t\tif !reflect.DeepEqual(m, tt.args.md) {\n\t\t\t\tt.Errorf(\"meta = %v, want %v\", m, tt.args.md)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestAppendToClientContext(t *testing.T) {\n\ttype args struct {\n\t\tmd Metadata\n\t\tkv []string\n\t}\n\ttests := []struct {\n\t\tname string\n\t\targs args\n\t\twant Metadata\n\t}{\n\t\t{\n\t\t\tname: \"kratos\",\n\t\t\targs: args{Metadata{}, []string{\"hello\", \"kratos\", \"env\", \"dev\"}},\n\t\t\twant: Metadata{\"hello\": {\"kratos\"}, \"env\": {\"dev\"}},\n\t\t},\n\t\t{\n\t\t\tname: \"hello\",\n\t\t\targs: args{Metadata{\"hi\": {\"https://go-kratos.dev/\"}}, []string{\"hello\", \"kratos\", \"env\", \"dev\"}},\n\t\t\twant: Metadata{\"hello\": {\"kratos\"}, \"env\": {\"dev\"}, \"hi\": {\"https://go-kratos.dev/\"}},\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tctx := NewClientContext(context.Background(), tt.args.md)\n\t\t\tctx = AppendToClientContext(ctx, tt.args.kv...)\n\t\t\tmd, ok := FromClientContext(ctx)\n\t\t\tif !ok {\n\t\t\t\tt.Errorf(\"FromServerContext() = %v, want %v\", ok, true)\n\t\t\t}\n\t\t\tif !reflect.DeepEqual(md, tt.want) {\n\t\t\t\tt.Errorf(\"metadata = %v, want %v\", md, tt.want)\n\t\t\t}\n\t\t})\n\t}\n}\n\n// nolint directives: sa5012\nfunc TestAppendToClientContextThatPanics(t *testing.T) {\n\tkvs := []string{\"hello\", \"kratos\", \"env\"}\n\tdefer func() {\n\t\tif r := recover(); r == nil {\n\t\t\tt.Errorf(\"append to client context singular kvs did not panic\")\n\t\t}\n\t}()\n\tctx := NewClientContext(context.Background(), Metadata{})\n\tctx = AppendToClientContext(ctx, kvs...)\n\tmd, ok := FromClientContext(ctx)\n\tif !ok {\n\t\tt.Errorf(\"FromServerContext() = %v, want %v\", ok, true)\n\t}\n\tif !reflect.DeepEqual(md, Metadata{}) {\n\t\tt.Errorf(\"metadata = %v, want %v\", md, Metadata{})\n\t}\n}\n\nfunc TestMergeToClientContext(t *testing.T) {\n\ttype args struct {\n\t\tmd       Metadata\n\t\tappendMd Metadata\n\t}\n\ttests := []struct {\n\t\tname string\n\t\targs args\n\t\twant Metadata\n\t}{\n\t\t{\n\t\t\tname: \"kratos\",\n\t\t\targs: args{Metadata{}, Metadata{\"hello\": {\"kratos\"}, \"env\": {\"dev\"}}},\n\t\t\twant: Metadata{\"hello\": {\"kratos\"}, \"env\": {\"dev\"}},\n\t\t},\n\t\t{\n\t\t\tname: \"hello\",\n\t\t\targs: args{Metadata{\"hi\": {\"https://go-kratos.dev/\"}}, Metadata{\"hello\": {\"kratos\"}, \"env\": {\"dev\"}}},\n\t\t\twant: Metadata{\"hello\": {\"kratos\"}, \"env\": {\"dev\"}, \"hi\": {\"https://go-kratos.dev/\"}},\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tctx := NewClientContext(context.Background(), tt.args.md)\n\t\t\tctx = MergeToClientContext(ctx, tt.args.appendMd)\n\t\t\tmd, ok := FromClientContext(ctx)\n\t\t\tif !ok {\n\t\t\t\tt.Errorf(\"FromServerContext() = %v, want %v\", ok, true)\n\t\t\t}\n\t\t\tif !reflect.DeepEqual(md, tt.want) {\n\t\t\t\tt.Errorf(\"metadata = %v, want %v\", md, tt.want)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestMetadata_Range(t *testing.T) {\n\tmd := Metadata{\"kratos\": {\"kratos\"}, \"https://go-kratos.dev/\": {\"https://go-kratos.dev/\"}, \"go-kratos\": {\"go-kratos\"}}\n\ttmp := Metadata{}\n\tmd.Range(func(k string, v []string) bool {\n\t\tif k == \"https://go-kratos.dev/\" || k == \"kratos\" {\n\t\t\ttmp[k] = v\n\t\t}\n\t\treturn true\n\t})\n\tif !reflect.DeepEqual(tmp, Metadata{\"https://go-kratos.dev/\": {\"https://go-kratos.dev/\"}, \"kratos\": {\"kratos\"}}) {\n\t\tt.Errorf(\"metadata = %v, want %v\", tmp, Metadata{\"https://go-kratos.dev/\": {\"https://go-kratos.dev/\"}, \"kratos\": {\"kratos\"}})\n\t}\n\ttmp = Metadata{}\n\tmd.Range(func(string, []string) bool {\n\t\treturn false\n\t})\n\tif !reflect.DeepEqual(tmp, Metadata{}) {\n\t\tt.Errorf(\"metadata = %v, want %v\", tmp, Metadata{})\n\t}\n}\n\nfunc TestMetadata_Clone(t *testing.T) {\n\ttests := []struct {\n\t\tname string\n\t\tm    Metadata\n\t\twant Metadata\n\t}{\n\t\t{\n\t\t\tname: \"kratos\",\n\t\t\tm:    Metadata{\"kratos\": {\"kratos\"}, \"https://go-kratos.dev/\": {\"https://go-kratos.dev/\"}, \"go-kratos\": {\"go-kratos\"}},\n\t\t\twant: Metadata{\"kratos\": {\"kratos\"}, \"https://go-kratos.dev/\": {\"https://go-kratos.dev/\"}, \"go-kratos\": {\"go-kratos\"}},\n\t\t},\n\t\t{\n\t\t\tname: \"go\",\n\t\t\tm:    Metadata{\"language\": {\"golang\"}},\n\t\t\twant: Metadata{\"language\": {\"golang\"}},\n\t\t},\n\t\t{\n\t\t\tname: \"plan9\",\n\t\t\tm:    Metadata{\"k0\": []string{}, \"k1\": nil},\n\t\t\twant: Metadata{\"k0\": []string{}, \"k1\": nil},\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tgot := tt.m.Clone()\n\t\t\tif !reflect.DeepEqual(got, tt.want) {\n\t\t\t\tt.Errorf(\"Clone() = %v, want %v\", got, tt.want)\n\t\t\t}\n\t\t\tgot[\"kratos\"] = []string{\"go\"}\n\t\t\tif reflect.DeepEqual(got, tt.want) {\n\t\t\t\tt.Errorf(\"want got != want got %v want %v\", got, tt.want)\n\t\t\t}\n\t\t})\n\t}\n}\n\n// TestMetadata_CloneDeepCopy tests that Clone creates a deep copy of metadata,\n// so modifications to the original metadata's slices don't affect the cloned one.\nfunc TestMetadata_CloneDeepCopy(t *testing.T) {\n\toriginal := Metadata{\n\t\t\"test-key\":   {\"value1\", \"value2\", \"value3\"},\n\t\t\"single-key\": {\"single-value\"},\n\t}\n\n\tcloned := original.Clone()\n\n\t// Test 1: Modify an element in the original metadata's slice\n\t{\n\t\toriginal[\"test-key\"][1] = \"modified-value\"\n\n\t\t// Verify that the cloned metadata's slice is not affected\n\t\tif cloned[\"test-key\"][1] != \"value2\" {\n\t\t\tt.Errorf(\"Clone() modify leaked: original=%v, cloned=%v\", original[\"test-key\"], cloned[\"test-key\"])\n\t\t}\n\t}\n\n\t// Test 2: Append to the original metadata's slice\n\t{\n\t\toriginal[\"test-key\"] = append(original[\"test-key\"], \"new-value\")\n\t\tif len(cloned[\"test-key\"]) != 3 {\n\t\t\tt.Errorf(\"Clone() append leaked: original len=%d, cloned len=%d\", len(original[\"test-key\"]), len(cloned[\"test-key\"]))\n\t\t}\n\t\texpected := []string{\"value1\", \"value2\", \"value3\"}\n\t\tif !reflect.DeepEqual(cloned[\"test-key\"], expected) {\n\t\t\tt.Errorf(\"Clone() append values: got=%v, want=%v\", cloned[\"test-key\"], expected)\n\t\t}\n\t}\n\n\t// Test 3: Replace the entire slice in the original metadata\n\t{\n\t\toriginal[\"single-key\"] = []string{\"replaced-value\"}\n\t\tif cloned[\"single-key\"][0] != \"single-value\" {\n\t\t\tt.Errorf(\"Clone() replace leaked: original=%v, cloned=%v\", original[\"single-key\"], cloned[\"single-key\"])\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "middleware/auth/jwt/jwt.go",
    "content": "package jwt\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"strings\"\n\n\t\"github.com/golang-jwt/jwt/v5\"\n\n\t\"github.com/go-kratos/kratos/v2/errors\"\n\t\"github.com/go-kratos/kratos/v2/middleware\"\n\t\"github.com/go-kratos/kratos/v2/transport\"\n)\n\ntype authKey struct{}\n\nconst (\n\n\t// bearerWord the bearer key word for authorization\n\tbearerWord string = \"Bearer\"\n\n\t// bearerFormat authorization token format\n\tbearerFormat string = \"Bearer %s\"\n\n\t// authorizationKey holds the key used to store the JWT Token in the request tokenHeader.\n\tauthorizationKey string = \"Authorization\"\n\n\t// reason holds the error reason.\n\treason string = \"UNAUTHORIZED\"\n)\n\nvar (\n\tErrMissingJwtToken        = errors.Unauthorized(reason, \"JWT token is missing\")\n\tErrMissingKeyFunc         = errors.Unauthorized(reason, \"keyFunc is missing\")\n\tErrTokenInvalid           = errors.Unauthorized(reason, \"Token is invalid\")\n\tErrTokenExpired           = errors.Unauthorized(reason, \"JWT token has expired\")\n\tErrTokenParseFail         = errors.Unauthorized(reason, \"Fail to parse JWT token \")\n\tErrUnSupportSigningMethod = errors.Unauthorized(reason, \"Wrong signing method\")\n\tErrWrongContext           = errors.Unauthorized(reason, \"Wrong context for middleware\")\n\tErrNeedTokenProvider      = errors.Unauthorized(reason, \"Token provider is missing\")\n\tErrSignToken              = errors.Unauthorized(reason, \"Can not sign token.Is the key correct?\")\n\tErrGetKey                 = errors.Unauthorized(reason, \"Can not get key while signing token\")\n)\n\n// Option is jwt option.\ntype Option func(*options)\n\n// Parser is a jwt parser\ntype options struct {\n\tsigningMethod jwt.SigningMethod\n\tclaims        func() jwt.Claims\n\ttokenHeader   map[string]any\n\tparserOptions []jwt.ParserOption\n}\n\n// WithSigningMethod with signing method option.\nfunc WithSigningMethod(method jwt.SigningMethod) Option {\n\treturn func(o *options) {\n\t\to.signingMethod = method\n\t}\n}\n\n// WithClaims with customer claim\n// If you use it in Server, f needs to return a new jwt.Claims object each time to avoid concurrent write problems\n// If you use it in Client, f only needs to return a single object to provide performance\nfunc WithClaims(f func() jwt.Claims) Option {\n\treturn func(o *options) {\n\t\to.claims = f\n\t}\n}\n\n// WithTokenHeader withe customer tokenHeader for client side\nfunc WithTokenHeader(header map[string]any) Option {\n\treturn func(o *options) {\n\t\to.tokenHeader = header\n\t}\n}\n\n// WithParserOptions with custom parser options for the jwt parser.\n// It allows customization of the jwt.Parser used during token validation,\n// for example, to enforce mandatory claim validations such as issuer, subject, or expiration.\nfunc WithParserOptions(opts ...jwt.ParserOption) Option {\n\treturn func(o *options) {\n\t\to.parserOptions = opts\n\t}\n}\n\n// Server is a server auth middleware. Check the token and extract the info from token.\nfunc Server(keyFunc jwt.Keyfunc, opts ...Option) middleware.Middleware {\n\to := &options{\n\t\tsigningMethod: jwt.SigningMethodHS256,\n\t}\n\tfor _, opt := range opts {\n\t\topt(o)\n\t}\n\treturn func(handler middleware.Handler) middleware.Handler {\n\t\treturn func(ctx context.Context, req any) (any, error) {\n\t\t\tif header, ok := transport.FromServerContext(ctx); ok {\n\t\t\t\tif keyFunc == nil {\n\t\t\t\t\treturn nil, ErrMissingKeyFunc\n\t\t\t\t}\n\t\t\t\tauths := strings.SplitN(header.RequestHeader().Get(authorizationKey), \" \", 2)\n\t\t\t\tif len(auths) != 2 || !strings.EqualFold(auths[0], bearerWord) {\n\t\t\t\t\treturn nil, ErrMissingJwtToken\n\t\t\t\t}\n\t\t\t\tjwtToken := auths[1]\n\t\t\t\tvar (\n\t\t\t\t\ttokenInfo *jwt.Token\n\t\t\t\t\terr       error\n\t\t\t\t)\n\t\t\t\tif o.claims != nil {\n\t\t\t\t\ttokenInfo, err = jwt.ParseWithClaims(jwtToken, o.claims(), keyFunc, o.parserOptions...)\n\t\t\t\t} else {\n\t\t\t\t\ttokenInfo, err = jwt.Parse(jwtToken, keyFunc, o.parserOptions...)\n\t\t\t\t}\n\t\t\t\tif err != nil {\n\t\t\t\t\tif errors.Is(err, jwt.ErrTokenMalformed) || errors.Is(err, jwt.ErrTokenUnverifiable) {\n\t\t\t\t\t\treturn nil, ErrTokenInvalid\n\t\t\t\t\t}\n\t\t\t\t\tif errors.Is(err, jwt.ErrTokenNotValidYet) || errors.Is(err, jwt.ErrTokenExpired) {\n\t\t\t\t\t\treturn nil, ErrTokenExpired\n\t\t\t\t\t}\n\t\t\t\t\treturn nil, ErrTokenParseFail\n\t\t\t\t}\n\n\t\t\t\tif !tokenInfo.Valid {\n\t\t\t\t\treturn nil, ErrTokenInvalid\n\t\t\t\t}\n\t\t\t\tif tokenInfo.Method != o.signingMethod {\n\t\t\t\t\treturn nil, ErrUnSupportSigningMethod\n\t\t\t\t}\n\t\t\t\tctx = NewContext(ctx, tokenInfo.Claims)\n\t\t\t\treturn handler(ctx, req)\n\t\t\t}\n\t\t\treturn nil, ErrWrongContext\n\t\t}\n\t}\n}\n\n// Client is a client jwt middleware.\nfunc Client(keyProvider jwt.Keyfunc, opts ...Option) middleware.Middleware {\n\tclaims := jwt.RegisteredClaims{}\n\to := &options{\n\t\tsigningMethod: jwt.SigningMethodHS256,\n\t\tclaims:        func() jwt.Claims { return claims },\n\t}\n\tfor _, opt := range opts {\n\t\topt(o)\n\t}\n\treturn func(handler middleware.Handler) middleware.Handler {\n\t\treturn func(ctx context.Context, req any) (any, error) {\n\t\t\tif keyProvider == nil {\n\t\t\t\treturn nil, ErrNeedTokenProvider\n\t\t\t}\n\t\t\ttoken := jwt.NewWithClaims(o.signingMethod, o.claims())\n\t\t\tif o.tokenHeader != nil {\n\t\t\t\tfor k, v := range o.tokenHeader {\n\t\t\t\t\ttoken.Header[k] = v\n\t\t\t\t}\n\t\t\t}\n\t\t\tkey, err := keyProvider(token)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, ErrGetKey\n\t\t\t}\n\t\t\ttokenStr, err := token.SignedString(key)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, ErrSignToken\n\t\t\t}\n\t\t\tif clientContext, ok := transport.FromClientContext(ctx); ok {\n\t\t\t\tclientContext.RequestHeader().Set(authorizationKey, fmt.Sprintf(bearerFormat, tokenStr))\n\t\t\t\treturn handler(ctx, req)\n\t\t\t}\n\t\t\treturn nil, ErrWrongContext\n\t\t}\n\t}\n}\n\n// NewContext put auth info into context\nfunc NewContext(ctx context.Context, info jwt.Claims) context.Context {\n\treturn context.WithValue(ctx, authKey{}, info)\n}\n\n// FromContext extract auth info from context\nfunc FromContext(ctx context.Context) (token jwt.Claims, ok bool) {\n\ttoken, ok = ctx.Value(authKey{}).(jwt.Claims)\n\treturn\n}\n"
  },
  {
    "path": "middleware/auth/jwt/jwt_test.go",
    "content": "package jwt\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"math/rand/v2\"\n\t\"net/http\"\n\t\"reflect\"\n\t\"strconv\"\n\t\"sync\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/golang-jwt/jwt/v5\"\n\n\t\"github.com/go-kratos/kratos/v2/middleware\"\n\t\"github.com/go-kratos/kratos/v2/transport\"\n)\n\ntype headerCarrier http.Header\n\nfunc (hc headerCarrier) Get(key string) string { return http.Header(hc).Get(key) }\n\nfunc (hc headerCarrier) Set(key string, value string) { http.Header(hc).Set(key, value) }\n\nfunc (hc headerCarrier) Add(key string, value string) { http.Header(hc).Add(key, value) }\n\n// Keys lists the keys stored in this carrier.\nfunc (hc headerCarrier) Keys() []string {\n\tkeys := make([]string, 0, len(hc))\n\tfor k := range http.Header(hc) {\n\t\tkeys = append(keys, k)\n\t}\n\treturn keys\n}\n\n// Values returns a slice value associated with the passed key.\nfunc (hc headerCarrier) Values(key string) []string {\n\treturn http.Header(hc).Values(key)\n}\n\nfunc newTokenHeader(headerKey string, token string) *headerCarrier {\n\theader := &headerCarrier{}\n\theader.Set(headerKey, token)\n\treturn header\n}\n\ntype Transport struct {\n\tkind      transport.Kind\n\tendpoint  string\n\toperation string\n\treqHeader transport.Header\n}\n\nfunc (tr *Transport) Kind() transport.Kind {\n\treturn tr.kind\n}\n\nfunc (tr *Transport) Endpoint() string {\n\treturn tr.endpoint\n}\n\nfunc (tr *Transport) Operation() string {\n\treturn tr.operation\n}\n\nfunc (tr *Transport) RequestHeader() transport.Header {\n\treturn tr.reqHeader\n}\n\nfunc (tr *Transport) ReplyHeader() transport.Header {\n\treturn nil\n}\n\ntype CustomerClaims struct {\n\tName string `json:\"name\"`\n\tjwt.RegisteredClaims\n}\n\nfunc TestJWTServerParse(t *testing.T) {\n\tvar (\n\t\terrConcurrentWrite = errors.New(\"concurrent write claims\")\n\t\terrParseClaims     = errors.New(\"bad result, token claims is not CustomerClaims\")\n\t)\n\n\ttestKey := \"testKey\"\n\ttests := []struct {\n\t\tname         string\n\t\ttoken        func() string\n\t\tclaims       func() jwt.Claims\n\t\texceptErr    error\n\t\tkey          string\n\t\tgoroutineNum int\n\t}{\n\t\t{\n\t\t\tname: \"normal\",\n\t\t\ttoken: func() string {\n\t\t\t\ttoken, err := jwt.NewWithClaims(jwt.SigningMethodHS256, &CustomerClaims{}).SignedString([]byte(testKey))\n\t\t\t\tif err != nil {\n\t\t\t\t\tpanic(err)\n\t\t\t\t}\n\t\t\t\treturn fmt.Sprintf(bearerFormat, token)\n\t\t\t},\n\t\t\tclaims: func() jwt.Claims {\n\t\t\t\treturn &CustomerClaims{}\n\t\t\t},\n\t\t\texceptErr:    nil,\n\t\t\tkey:          testKey,\n\t\t\tgoroutineNum: 1,\n\t\t},\n\t\t{\n\t\t\tname: \"concurrent request\",\n\t\t\ttoken: func() string {\n\t\t\t\ttoken, err := jwt.NewWithClaims(jwt.SigningMethodHS256, &CustomerClaims{\n\t\t\t\t\tName: strconv.Itoa(rand.Int()),\n\t\t\t\t}).SignedString([]byte(testKey))\n\t\t\t\tif err != nil {\n\t\t\t\t\tpanic(err)\n\t\t\t\t}\n\t\t\t\treturn fmt.Sprintf(bearerFormat, token)\n\t\t\t},\n\t\t\tclaims: func() jwt.Claims {\n\t\t\t\treturn &CustomerClaims{}\n\t\t\t},\n\t\t\texceptErr:    nil,\n\t\t\tkey:          testKey,\n\t\t\tgoroutineNum: 10000,\n\t\t},\n\t}\n\n\tnext := func(ctx context.Context, _ any) (any, error) {\n\t\ttestToken, _ := FromContext(ctx)\n\t\tvar name string\n\t\tif customerClaims, ok := testToken.(*CustomerClaims); ok {\n\t\t\tname = customerClaims.Name\n\t\t} else {\n\t\t\treturn nil, errParseClaims\n\t\t}\n\n\t\t// mock biz\n\t\ttime.Sleep(100 * time.Millisecond)\n\n\t\tif customerClaims, ok := testToken.(*CustomerClaims); ok {\n\t\t\tif name != customerClaims.Name {\n\t\t\t\treturn nil, errConcurrentWrite\n\t\t\t}\n\t\t} else {\n\t\t\treturn nil, errParseClaims\n\t\t}\n\t\treturn \"reply\", nil\n\t}\n\n\tfor _, test := range tests {\n\t\tt.Run(test.name, func(t *testing.T) {\n\t\t\tserver := Server(\n\t\t\t\tfunc(*jwt.Token) (any, error) { return []byte(testKey), nil },\n\t\t\t\tWithClaims(test.claims),\n\t\t\t)(next)\n\t\t\twg := sync.WaitGroup{}\n\t\t\tfor i := 0; i < test.goroutineNum; i++ {\n\t\t\t\twg.Add(1)\n\t\t\t\tgo func() {\n\t\t\t\t\tdefer wg.Done()\n\t\t\t\t\tctx := transport.NewServerContext(context.Background(), &Transport{reqHeader: newTokenHeader(authorizationKey, test.token())})\n\t\t\t\t\t_, err2 := server(ctx, test.name)\n\t\t\t\t\tif !errors.Is(test.exceptErr, err2) {\n\t\t\t\t\t\tt.Errorf(\"except error %v, but got %v\", test.exceptErr, err2)\n\t\t\t\t\t}\n\t\t\t\t}()\n\t\t\t}\n\t\t\twg.Wait()\n\t\t})\n\t}\n}\n\nfunc TestServer(t *testing.T) {\n\ttestKey := \"testKey\"\n\tmapClaims := jwt.MapClaims{}\n\tmapClaims[\"name\"] = \"xiaoli\"\n\tclaims := jwt.NewWithClaims(jwt.SigningMethodHS256, mapClaims)\n\ttoken, err := claims.SignedString([]byte(testKey))\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\ttoken = fmt.Sprintf(bearerFormat, token)\n\ttests := []struct {\n\t\tname          string\n\t\tctx           context.Context\n\t\tsigningMethod jwt.SigningMethod\n\t\texceptErr     error\n\t\tkey           string\n\t}{\n\t\t{\n\t\t\tname:          \"normal\",\n\t\t\tctx:           transport.NewServerContext(context.Background(), &Transport{reqHeader: newTokenHeader(authorizationKey, token)}),\n\t\t\tsigningMethod: jwt.SigningMethodHS256,\n\t\t\texceptErr:     nil,\n\t\t\tkey:           testKey,\n\t\t},\n\t\t{\n\t\t\tname:          \"miss token\",\n\t\t\tctx:           transport.NewServerContext(context.Background(), &Transport{reqHeader: headerCarrier{}}),\n\t\t\tsigningMethod: jwt.SigningMethodHS256,\n\t\t\texceptErr:     ErrMissingJwtToken,\n\t\t\tkey:           testKey,\n\t\t},\n\t\t{\n\t\t\tname: \"token invalid\",\n\t\t\tctx: transport.NewServerContext(context.Background(), &Transport{\n\t\t\t\treqHeader: newTokenHeader(authorizationKey, fmt.Sprintf(bearerFormat, \"12313123\")),\n\t\t\t}),\n\t\t\tsigningMethod: jwt.SigningMethodHS256,\n\t\t\texceptErr:     ErrTokenInvalid,\n\t\t\tkey:           testKey,\n\t\t},\n\t\t{\n\t\t\tname:          \"method invalid\",\n\t\t\tctx:           transport.NewServerContext(context.Background(), &Transport{reqHeader: newTokenHeader(authorizationKey, token)}),\n\t\t\tsigningMethod: jwt.SigningMethodES384,\n\t\t\texceptErr:     ErrUnSupportSigningMethod,\n\t\t\tkey:           testKey,\n\t\t},\n\t\t{\n\t\t\tname:          \"miss signing method\",\n\t\t\tctx:           transport.NewServerContext(context.Background(), &Transport{reqHeader: newTokenHeader(authorizationKey, token)}),\n\t\t\tsigningMethod: nil,\n\t\t\texceptErr:     nil,\n\t\t\tkey:           testKey,\n\t\t},\n\t\t{\n\t\t\tname:          \"miss signing method\",\n\t\t\tctx:           transport.NewServerContext(context.Background(), &Transport{reqHeader: newTokenHeader(authorizationKey, token)}),\n\t\t\tsigningMethod: nil,\n\t\t\texceptErr:     nil,\n\t\t\tkey:           testKey,\n\t\t},\n\t}\n\n\tfor _, test := range tests {\n\t\tt.Run(test.name, func(t *testing.T) {\n\t\t\tvar testToken jwt.Claims\n\t\t\tnext := func(ctx context.Context, req any) (any, error) {\n\t\t\t\tt.Log(req)\n\t\t\t\ttestToken, _ = FromContext(ctx)\n\t\t\t\treturn \"reply\", nil\n\t\t\t}\n\t\t\tvar server middleware.Handler\n\t\t\tif test.signingMethod != nil {\n\t\t\t\tserver = Server(func(*jwt.Token) (any, error) {\n\t\t\t\t\treturn []byte(test.key), nil\n\t\t\t\t}, WithSigningMethod(test.signingMethod))(next)\n\t\t\t} else {\n\t\t\t\tserver = Server(func(*jwt.Token) (any, error) {\n\t\t\t\t\treturn []byte(test.key), nil\n\t\t\t\t})(next)\n\t\t\t}\n\t\t\t_, err2 := server(test.ctx, test.name)\n\t\t\tif !errors.Is(test.exceptErr, err2) {\n\t\t\t\tt.Errorf(\"except error %v, but got %v\", test.exceptErr, err2)\n\t\t\t}\n\t\t\tif test.exceptErr == nil {\n\t\t\t\tif testToken == nil {\n\t\t\t\t\tt.Fatal(\"except testToken not nil, but got nil\")\n\t\t\t\t}\n\t\t\t\t_, ok := testToken.(jwt.MapClaims)\n\t\t\t\tif !ok {\n\t\t\t\t\tt.Errorf(\"except testToken is jwt.MapClaims, but got %T\", testToken)\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestClient(t *testing.T) {\n\ttestKey := \"testKey\"\n\n\tclaims := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.RegisteredClaims{})\n\ttoken, err := claims.SignedString([]byte(testKey))\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\ttProvider := func(*jwt.Token) (any, error) {\n\t\treturn []byte(testKey), nil\n\t}\n\ttests := []struct {\n\t\tname          string\n\t\texpectError   error\n\t\ttokenProvider jwt.Keyfunc\n\t}{\n\t\t{\n\t\t\tname:          \"normal\",\n\t\t\texpectError:   nil,\n\t\t\ttokenProvider: tProvider,\n\t\t},\n\t\t{\n\t\t\tname:          \"miss token provider\",\n\t\t\texpectError:   ErrNeedTokenProvider,\n\t\t\ttokenProvider: nil,\n\t\t},\n\t}\n\tfor _, test := range tests {\n\t\tt.Run(test.name, func(t *testing.T) {\n\t\t\tnext := func(context.Context, any) (any, error) {\n\t\t\t\treturn \"reply\", nil\n\t\t\t}\n\t\t\thandler := Client(test.tokenProvider)(next)\n\t\t\theader := &headerCarrier{}\n\t\t\t_, err2 := handler(transport.NewClientContext(context.Background(), &Transport{reqHeader: header}), \"ok\")\n\t\t\tif !errors.Is(test.expectError, err2) {\n\t\t\t\tt.Errorf(\"except error %v, but got %v\", test.expectError, err2)\n\t\t\t}\n\t\t\tif err2 == nil {\n\t\t\t\tif !reflect.DeepEqual(header.Get(authorizationKey), fmt.Sprintf(bearerFormat, token)) {\n\t\t\t\t\tt.Errorf(\"except header %s, but got %s\", fmt.Sprintf(bearerFormat, token), header.Get(authorizationKey))\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestTokenExpire(t *testing.T) {\n\ttestKey := \"testKey\"\n\tclaims := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.RegisteredClaims{\n\t\tExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Millisecond)),\n\t})\n\ttoken, err := claims.SignedString([]byte(testKey))\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\ttoken = fmt.Sprintf(bearerFormat, token)\n\ttime.Sleep(time.Second)\n\tnext := func(_ context.Context, req any) (any, error) {\n\t\tt.Log(req)\n\t\treturn \"reply\", nil\n\t}\n\tctx := transport.NewServerContext(context.Background(), &Transport{reqHeader: newTokenHeader(authorizationKey, token)})\n\tserver := Server(func(*jwt.Token) (any, error) {\n\t\treturn []byte(testKey), nil\n\t}, WithSigningMethod(jwt.SigningMethodHS256))(next)\n\t_, err2 := server(ctx, \"test expire token\")\n\tif !errors.Is(ErrTokenExpired, err2) {\n\t\tt.Errorf(\"except error %v, but got %v\", ErrTokenExpired, err2)\n\t}\n}\n\nfunc TestMissingKeyFunc(t *testing.T) {\n\ttestKey := \"testKey\"\n\tmapClaims := jwt.MapClaims{}\n\tmapClaims[\"name\"] = \"xiaoli\"\n\tclaims := jwt.NewWithClaims(jwt.SigningMethodHS256, mapClaims)\n\ttoken, err := claims.SignedString([]byte(testKey))\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\ttoken = fmt.Sprintf(bearerFormat, token)\n\ttest := struct {\n\t\tname          string\n\t\tctx           context.Context\n\t\tsigningMethod jwt.SigningMethod\n\t\texceptErr     error\n\t\tkey           string\n\t}{\n\t\tname:          \"miss key\",\n\t\tctx:           transport.NewServerContext(context.Background(), &Transport{reqHeader: newTokenHeader(authorizationKey, token)}),\n\t\tsigningMethod: jwt.SigningMethodHS256,\n\t\texceptErr:     ErrMissingKeyFunc,\n\t\tkey:           \"\",\n\t}\n\n\tvar testToken jwt.Claims\n\tnext := func(ctx context.Context, req any) (any, error) {\n\t\tt.Log(req)\n\t\ttestToken, _ = FromContext(ctx)\n\t\treturn \"reply\", nil\n\t}\n\tserver := Server(nil)(next)\n\t_, err2 := server(test.ctx, test.name)\n\tif !errors.Is(test.exceptErr, err2) {\n\t\tt.Errorf(\"except error %v, but got %v\", test.exceptErr, err2)\n\t}\n\tif test.exceptErr == nil {\n\t\tif testToken == nil {\n\t\t\tt.Errorf(\"except testToken not nil, but got nil\")\n\t\t}\n\t}\n}\n\nfunc TestClientWithClaims(t *testing.T) {\n\ttestKey := \"testKey\"\n\tmapClaims := jwt.MapClaims{}\n\tmapClaims[\"name\"] = \"xiaoli\"\n\tmapClaimsFunc := func() jwt.Claims { return mapClaims }\n\tclaims := jwt.NewWithClaims(jwt.SigningMethodHS256, mapClaims)\n\ttoken, err := claims.SignedString([]byte(testKey))\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\ttProvider := func(*jwt.Token) (any, error) {\n\t\treturn []byte(testKey), nil\n\t}\n\ttest := struct {\n\t\tname          string\n\t\texpectError   error\n\t\ttokenProvider jwt.Keyfunc\n\t}{\n\t\tname:          \"normal\",\n\t\texpectError:   nil,\n\t\ttokenProvider: tProvider,\n\t}\n\n\tt.Run(test.name, func(t *testing.T) {\n\t\tnext := func(context.Context, any) (any, error) {\n\t\t\treturn \"reply\", nil\n\t\t}\n\t\thandler := Client(test.tokenProvider, WithClaims(mapClaimsFunc))(next)\n\t\theader := &headerCarrier{}\n\t\t_, err2 := handler(transport.NewClientContext(context.Background(), &Transport{reqHeader: header}), \"ok\")\n\t\tif !errors.Is(test.expectError, err2) {\n\t\t\tt.Errorf(\"except error %v, but got %v\", test.expectError, err2)\n\t\t}\n\t\tif err2 == nil {\n\t\t\tif !reflect.DeepEqual(header.Get(authorizationKey), fmt.Sprintf(bearerFormat, token)) {\n\t\t\t\tt.Errorf(\"except header %s, but got %s\", fmt.Sprintf(bearerFormat, token), header.Get(authorizationKey))\n\t\t\t}\n\t\t}\n\t})\n}\n\nfunc TestClientWithHeader(t *testing.T) {\n\ttestKey := \"testKey\"\n\tmapClaims := jwt.MapClaims{}\n\tmapClaims[\"name\"] = \"xiaoli\"\n\tmapClaimsFunc := func() jwt.Claims { return mapClaims }\n\ttokenHeader := map[string]any{\n\t\t\"test\": \"test\",\n\t}\n\tclaims := jwt.NewWithClaims(jwt.SigningMethodHS256, mapClaims)\n\tfor k, v := range tokenHeader {\n\t\tclaims.Header[k] = v\n\t}\n\ttoken, err := claims.SignedString([]byte(testKey))\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\ttProvider := func(*jwt.Token) (any, error) {\n\t\treturn []byte(testKey), nil\n\t}\n\tnext := func(context.Context, any) (any, error) {\n\t\treturn \"reply\", nil\n\t}\n\thandler := Client(tProvider, WithClaims(mapClaimsFunc), WithTokenHeader(tokenHeader))(next)\n\theader := &headerCarrier{}\n\t_, err2 := handler(transport.NewClientContext(context.Background(), &Transport{reqHeader: header}), \"ok\")\n\tif err2 != nil {\n\t\tt.Errorf(\"except error nil, but got %v\", err2)\n\t}\n\tif !reflect.DeepEqual(header.Get(authorizationKey), fmt.Sprintf(bearerFormat, token)) {\n\t\tt.Errorf(\"except header %s, but got %s\", fmt.Sprintf(bearerFormat, token), header.Get(authorizationKey))\n\t}\n}\n\nfunc TestClientMissKey(t *testing.T) {\n\ttestKey := \"testKey\"\n\tmapClaims := jwt.MapClaims{}\n\tmapClaims[\"name\"] = \"xiaoli\"\n\tmapClaimsFunc := func() jwt.Claims { return mapClaims }\n\tclaims := jwt.NewWithClaims(jwt.SigningMethodHS256, mapClaims)\n\ttoken, err := claims.SignedString([]byte(testKey))\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\ttProvider := func(*jwt.Token) (any, error) {\n\t\treturn nil, errors.New(\"some error\")\n\t}\n\ttest := struct {\n\t\tname          string\n\t\texpectError   error\n\t\ttokenProvider jwt.Keyfunc\n\t}{\n\t\tname:          \"normal\",\n\t\texpectError:   ErrGetKey,\n\t\ttokenProvider: tProvider,\n\t}\n\n\tt.Run(test.name, func(t *testing.T) {\n\t\tnext := func(context.Context, any) (any, error) {\n\t\t\treturn \"reply\", nil\n\t\t}\n\t\thandler := Client(test.tokenProvider, WithClaims(mapClaimsFunc))(next)\n\t\theader := &headerCarrier{}\n\t\t_, err2 := handler(transport.NewClientContext(context.Background(), &Transport{reqHeader: header}), \"ok\")\n\t\tif !errors.Is(test.expectError, err2) {\n\t\t\tt.Errorf(\"except error %v, but got %v\", test.expectError, err2)\n\t\t}\n\t\tif err2 == nil {\n\t\t\tif !reflect.DeepEqual(header.Get(authorizationKey), fmt.Sprintf(bearerFormat, token)) {\n\t\t\t\tt.Errorf(\"except header %s, but got %s\", fmt.Sprintf(bearerFormat, token), header.Get(authorizationKey))\n\t\t\t}\n\t\t}\n\t})\n}\n\nfunc TestNewContextAndFromContext(t *testing.T) {\n\ttests := []struct {\n\t\tname   string\n\t\tclaims jwt.MapClaims\n\t}{\n\t\t{\"val not nil\", jwt.MapClaims{\"name\": \"kratos\"}},\n\t\t{\"val nil\", nil},\n\t}\n\tfor _, test := range tests {\n\t\tt.Run(test.name, func(t *testing.T) {\n\t\t\tctx := NewContext(context.Background(), test.claims)\n\n\t\t\tclaims, ok := FromContext(ctx)\n\t\t\tif !ok {\n\t\t\t\tt.Fatal(\"ctx not found authKey{}\")\n\t\t\t}\n\t\t\tif !reflect.DeepEqual(test.claims, claims) {\n\t\t\t\tt.Errorf(`want: %s, got: %v`, test.claims, claims)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestWithParserOptions(t *testing.T) {\n\ttestKey := \"testKey\"\n\tissuer := \"https://example.com\"\n\tsubject := \"user\"\n\n\tnext := func(ctx context.Context, _ any) (any, error) {\n\t\ttestToken, _ := FromContext(ctx)\n\t\tif testToken == nil {\n\t\t\tt.Error(\"expected testToken not nil, but got nil\")\n\t\t}\n\t\treturn \"reply\", nil\n\t}\n\n\ttests := []struct {\n\t\tname          string\n\t\tclaims        jwt.RegisteredClaims\n\t\tparserOptions []jwt.ParserOption\n\t\tcustomClaims  func() jwt.Claims\n\t\texceptErr     error\n\t}{\n\t\t{\n\t\t\tname: \"valid token with matching issuer\",\n\t\t\tclaims: jwt.RegisteredClaims{\n\t\t\t\tIssuer:    issuer,\n\t\t\t\tExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour)),\n\t\t\t},\n\t\t\tparserOptions: []jwt.ParserOption{\n\t\t\t\tjwt.WithIssuer(issuer),\n\t\t\t},\n\t\t\texceptErr: nil,\n\t\t},\n\t\t{\n\t\t\tname: \"invalid token with wrong issuer\",\n\t\t\tclaims: jwt.RegisteredClaims{\n\t\t\t\tIssuer:    \"https://wrong-issuer.com\",\n\t\t\t\tExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour)),\n\t\t\t},\n\t\t\tparserOptions: []jwt.ParserOption{\n\t\t\t\tjwt.WithIssuer(issuer),\n\t\t\t},\n\t\t\texceptErr: ErrTokenParseFail,\n\t\t},\n\t\t{\n\t\t\tname: \"valid token with matching subject\",\n\t\t\tclaims: jwt.RegisteredClaims{\n\t\t\t\tSubject:   subject,\n\t\t\t\tExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour)),\n\t\t\t},\n\t\t\tparserOptions: []jwt.ParserOption{\n\t\t\t\tjwt.WithSubject(subject),\n\t\t\t},\n\t\t\texceptErr: nil,\n\t\t},\n\t\t{\n\t\t\tname: \"invalid token with wrong subject\",\n\t\t\tclaims: jwt.RegisteredClaims{\n\t\t\t\tSubject:   \"admin\",\n\t\t\t\tExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour)),\n\t\t\t},\n\t\t\tparserOptions: []jwt.ParserOption{\n\t\t\t\tjwt.WithSubject(subject),\n\t\t\t},\n\t\t\texceptErr: ErrTokenParseFail,\n\t\t},\n\t\t{\n\t\t\tname: \"valid token with multiple parser options\",\n\t\t\tclaims: jwt.RegisteredClaims{\n\t\t\t\tIssuer:    issuer,\n\t\t\t\tSubject:   subject,\n\t\t\t\tExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour)),\n\t\t\t},\n\t\t\tparserOptions: []jwt.ParserOption{\n\t\t\t\tjwt.WithIssuer(issuer),\n\t\t\t\tjwt.WithSubject(subject),\n\t\t\t\tjwt.WithExpirationRequired(),\n\t\t\t},\n\t\t\texceptErr: nil,\n\t\t},\n\t\t{\n\t\t\tname: \"invalid token missing required expiration\",\n\t\t\tclaims: jwt.RegisteredClaims{\n\t\t\t\tIssuer:  issuer,\n\t\t\t\tSubject: subject,\n\t\t\t},\n\t\t\tparserOptions: []jwt.ParserOption{\n\t\t\t\tjwt.WithExpirationRequired(),\n\t\t\t},\n\t\t\texceptErr: ErrTokenParseFail,\n\t\t},\n\t\t{\n\t\t\tname: \"valid token with no parser options (backward compatibility)\",\n\t\t\tclaims: jwt.RegisteredClaims{\n\t\t\t\tExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour)),\n\t\t\t},\n\t\t\tparserOptions: nil,\n\t\t\texceptErr:     nil,\n\t\t},\n\t\t{\n\t\t\tname: \"valid token with custom claims and matching issuer\",\n\t\t\tclaims: jwt.RegisteredClaims{\n\t\t\t\tIssuer:    issuer,\n\t\t\t\tExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour)),\n\t\t\t},\n\t\t\tparserOptions: []jwt.ParserOption{\n\t\t\t\tjwt.WithIssuer(issuer),\n\t\t\t},\n\t\t\tcustomClaims: func() jwt.Claims {\n\t\t\t\treturn &CustomerClaims{}\n\t\t\t},\n\t\t\texceptErr: nil,\n\t\t},\n\t\t{\n\t\t\tname: \"invalid token with custom claims and wrong issuer\",\n\t\t\tclaims: jwt.RegisteredClaims{\n\t\t\t\tIssuer:    \"https://wrong-issuer.com\",\n\t\t\t\tExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour)),\n\t\t\t},\n\t\t\tparserOptions: []jwt.ParserOption{\n\t\t\t\tjwt.WithIssuer(issuer),\n\t\t\t},\n\t\t\tcustomClaims: func() jwt.Claims {\n\t\t\t\treturn &CustomerClaims{}\n\t\t\t},\n\t\t\texceptErr: ErrTokenParseFail,\n\t\t},\n\t}\n\n\tfor _, test := range tests {\n\t\tt.Run(test.name, func(t *testing.T) {\n\t\t\ttoken, err := jwt.NewWithClaims(jwt.SigningMethodHS256, test.claims).SignedString([]byte(testKey))\n\t\t\tif err != nil {\n\t\t\t\tt.Fatal(err)\n\t\t\t}\n\t\t\tctx := transport.NewServerContext(context.Background(), &Transport{\n\t\t\t\treqHeader: newTokenHeader(authorizationKey, fmt.Sprintf(bearerFormat, token)),\n\t\t\t})\n\n\t\t\topts := []Option{WithSigningMethod(jwt.SigningMethodHS256)}\n\t\t\tif test.parserOptions != nil {\n\t\t\t\topts = append(opts, WithParserOptions(test.parserOptions...))\n\t\t\t}\n\t\t\tif test.customClaims != nil {\n\t\t\t\topts = append(opts, WithClaims(test.customClaims))\n\t\t\t}\n\n\t\t\tserver := Server(\n\t\t\t\tfunc(*jwt.Token) (any, error) { return []byte(testKey), nil },\n\t\t\t\topts...,\n\t\t\t)(next)\n\n\t\t\t_, err2 := server(ctx, test.name)\n\t\t\tif !errors.Is(test.exceptErr, err2) {\n\t\t\t\tt.Errorf(\"expected error %v, but got %v\", test.exceptErr, err2)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestWithParserOptionsConcurrent(t *testing.T) {\n\ttestKey := \"testKey\"\n\tissuer := \"https://example.com\"\n\n\tnext := func(ctx context.Context, _ any) (any, error) {\n\t\ttestToken, _ := FromContext(ctx)\n\t\tif testToken == nil {\n\t\t\treturn nil, errors.New(\"expected testToken not nil, but got nil\")\n\t\t}\n\t\treturn \"reply\", nil\n\t}\n\n\tserver := Server(\n\t\tfunc(*jwt.Token) (any, error) { return []byte(testKey), nil },\n\t\tWithClaims(func() jwt.Claims { return &CustomerClaims{} }),\n\t\tWithParserOptions(jwt.WithIssuer(issuer), jwt.WithExpirationRequired()),\n\t)(next)\n\n\twg := sync.WaitGroup{}\n\tfor i := 0; i < 100; i++ {\n\t\twg.Add(1)\n\t\tgo func() {\n\t\t\tdefer wg.Done()\n\t\t\ttoken, err := jwt.NewWithClaims(jwt.SigningMethodHS256, &CustomerClaims{\n\t\t\t\tName: strconv.Itoa(rand.Int()),\n\t\t\t\tRegisteredClaims: jwt.RegisteredClaims{\n\t\t\t\t\tIssuer:    issuer,\n\t\t\t\t\tExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour)),\n\t\t\t\t},\n\t\t\t}).SignedString([]byte(testKey))\n\t\t\tif err != nil {\n\t\t\t\tt.Error(err)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tctx := transport.NewServerContext(context.Background(), &Transport{\n\t\t\t\treqHeader: newTokenHeader(authorizationKey, fmt.Sprintf(bearerFormat, token)),\n\t\t\t})\n\t\t\t_, err2 := server(ctx, \"concurrent\")\n\t\t\tif err2 != nil {\n\t\t\t\tt.Errorf(\"expected nil error, but got %v\", err2)\n\t\t\t}\n\t\t}()\n\t}\n\twg.Wait()\n}\n\nfunc TestWithParserOptionsEmpty(t *testing.T) {\n\ttestKey := \"testKey\"\n\n\tnext := func(_ context.Context, _ any) (any, error) {\n\t\treturn \"reply\", nil\n\t}\n\n\ttoken, err := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{\n\t\t\"name\": \"kratos\",\n\t}).SignedString([]byte(testKey))\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tctx := transport.NewServerContext(context.Background(), &Transport{\n\t\treqHeader: newTokenHeader(authorizationKey, fmt.Sprintf(bearerFormat, token)),\n\t})\n\n\t// WithParserOptions called with no arguments should behave identically to not calling it.\n\tserver := Server(\n\t\tfunc(*jwt.Token) (any, error) { return []byte(testKey), nil },\n\t\tWithParserOptions(),\n\t)(next)\n\n\t_, err2 := server(ctx, \"empty parser options\")\n\tif err2 != nil {\n\t\tt.Errorf(\"expected nil error, but got %v\", err2)\n\t}\n}\n"
  },
  {
    "path": "middleware/circuitbreaker/circuitbreaker.go",
    "content": "package circuitbreaker\n\nimport (\n\t\"context\"\n\n\t\"github.com/go-kratos/aegis/circuitbreaker\"\n\t\"github.com/go-kratos/aegis/circuitbreaker/sre\"\n\n\t\"github.com/go-kratos/kratos/v2/errors\"\n\t\"github.com/go-kratos/kratos/v2/internal/group\"\n\t\"github.com/go-kratos/kratos/v2/middleware\"\n\t\"github.com/go-kratos/kratos/v2/transport\"\n)\n\n// ErrNotAllowed is request failed due to circuit breaker triggered.\nvar ErrNotAllowed = errors.New(503, \"CIRCUITBREAKER\", \"request failed due to circuit breaker triggered\")\n\n// Option is circuit breaker option.\ntype Option func(*options)\n\n// WithGroup with circuit breaker group.\n// NOTE: implements generics circuitbreaker.CircuitBreaker\nfunc WithGroup(g *group.Group[circuitbreaker.CircuitBreaker]) Option {\n\treturn func(o *options) {\n\t\to.group = g\n\t}\n}\n\n// WithCircuitBreaker with circuit breaker genFunc.\nfunc WithCircuitBreaker(genBreakerFunc func() circuitbreaker.CircuitBreaker) Option {\n\treturn func(o *options) {\n\t\to.group = group.NewGroup(func() circuitbreaker.CircuitBreaker {\n\t\t\treturn genBreakerFunc()\n\t\t})\n\t}\n}\n\ntype options struct {\n\tgroup *group.Group[circuitbreaker.CircuitBreaker]\n}\n\n// Client circuitbreaker middleware will return errBreakerTriggered when the circuit\n// breaker is triggered and the request is rejected directly.\nfunc Client(opts ...Option) middleware.Middleware {\n\topt := &options{\n\t\tgroup: group.NewGroup(func() circuitbreaker.CircuitBreaker {\n\t\t\treturn sre.NewBreaker()\n\t\t}),\n\t}\n\tfor _, o := range opts {\n\t\to(opt)\n\t}\n\treturn func(handler middleware.Handler) middleware.Handler {\n\t\treturn func(ctx context.Context, req any) (any, error) {\n\t\t\tinfo, _ := transport.FromClientContext(ctx)\n\t\t\tbreaker := opt.group.Get(info.Operation())\n\t\t\tif err := breaker.Allow(); err != nil {\n\t\t\t\t// rejected\n\t\t\t\t// NOTE: when client reject requests locally,\n\t\t\t\t// continue to add counter let the drop ratio higher.\n\t\t\t\tbreaker.MarkFailed()\n\t\t\t\treturn nil, ErrNotAllowed\n\t\t\t}\n\t\t\t// allowed\n\t\t\treply, err := handler(ctx, req)\n\t\t\tif err != nil && (errors.IsInternalServer(err) || errors.IsServiceUnavailable(err) || errors.IsGatewayTimeout(err)) {\n\t\t\t\tbreaker.MarkFailed()\n\t\t\t} else {\n\t\t\t\tbreaker.MarkSuccess()\n\t\t\t}\n\t\t\treturn reply, err\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "middleware/circuitbreaker/circuitbreaker_test.go",
    "content": "package circuitbreaker\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"testing\"\n\n\t\"github.com/go-kratos/aegis/circuitbreaker\"\n\tkratoserrors \"github.com/go-kratos/kratos/v2/errors\"\n\t\"github.com/go-kratos/kratos/v2/internal/group\"\n\t\"github.com/go-kratos/kratos/v2/transport\"\n)\n\ntype transportMock struct {\n\tkind      transport.Kind\n\tendpoint  string\n\toperation string\n}\n\ntype circuitBreakerMock struct {\n\terr error\n}\n\nfunc (tr *transportMock) Kind() transport.Kind {\n\treturn tr.kind\n}\n\nfunc (tr *transportMock) Endpoint() string {\n\treturn tr.endpoint\n}\n\nfunc (tr *transportMock) Operation() string {\n\treturn tr.operation\n}\n\nfunc (tr *transportMock) RequestHeader() transport.Header {\n\treturn nil\n}\n\nfunc (tr *transportMock) ReplyHeader() transport.Header {\n\treturn nil\n}\n\nfunc (c *circuitBreakerMock) Allow() error { return c.err }\nfunc (c *circuitBreakerMock) MarkSuccess() {}\nfunc (c *circuitBreakerMock) MarkFailed()  {}\n\nfunc Test_WithGroup(t *testing.T) {\n\to := options{\n\t\tgroup: group.NewGroup(func() circuitbreaker.CircuitBreaker {\n\t\t\treturn &circuitBreakerMock{}\n\t\t}),\n\t}\n\n\tWithGroup(nil)(&o)\n\tif o.group != nil {\n\t\tt.Error(\"The group property must be updated to nil.\")\n\t}\n}\n\nfunc TestServer(_ *testing.T) {\n\tnextValid := func(context.Context, any) (any, error) {\n\t\treturn \"Hello valid\", nil\n\t}\n\tnextInvalid := func(context.Context, any) (any, error) {\n\t\treturn nil, kratoserrors.InternalServer(\"\", \"\")\n\t}\n\n\tctx := transport.NewClientContext(context.Background(), &transportMock{})\n\n\t_, _ = Client(func(o *options) {\n\t\to.group = group.NewGroup(func() circuitbreaker.CircuitBreaker {\n\t\t\treturn &circuitBreakerMock{err: errors.New(\"circuitbreaker error\")}\n\t\t})\n\t})(nextValid)(ctx, nil)\n\n\t_, _ = Client(func(_ *options) {})(nextValid)(ctx, nil)\n\n\t_, _ = Client(func(_ *options) {})(nextInvalid)(ctx, nil)\n}\n"
  },
  {
    "path": "middleware/logging/logging.go",
    "content": "package logging\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"time\"\n\n\t\"google.golang.org/grpc/codes\"\n\n\t\"github.com/go-kratos/kratos/v2/errors\"\n\t\"github.com/go-kratos/kratos/v2/log\"\n\t\"github.com/go-kratos/kratos/v2/middleware\"\n\t\"github.com/go-kratos/kratos/v2/transport\"\n\t\"github.com/go-kratos/kratos/v2/transport/http/status\"\n)\n\n// Redacter defines how to log an object\ntype Redacter interface {\n\tRedact() string\n}\n\n// Server is an server logging middleware.\nfunc Server(logger log.Logger) middleware.Middleware {\n\treturn func(handler middleware.Handler) middleware.Handler {\n\t\treturn func(ctx context.Context, req any) (reply any, err error) {\n\t\t\tvar (\n\t\t\t\tcode      int32\n\t\t\t\treason    string\n\t\t\t\tkind      string\n\t\t\t\toperation string\n\t\t\t)\n\n\t\t\t// default code\n\t\t\tcode = int32(status.FromGRPCCode(codes.OK))\n\n\t\t\tstartTime := time.Now()\n\t\t\tif info, ok := transport.FromServerContext(ctx); ok {\n\t\t\t\tkind = info.Kind().String()\n\t\t\t\toperation = info.Operation()\n\t\t\t}\n\t\t\treply, err = handler(ctx, req)\n\t\t\tif se := errors.FromError(err); se != nil {\n\t\t\t\tcode = se.Code\n\t\t\t\treason = se.Reason\n\t\t\t}\n\t\t\tlevel, stack := extractError(err)\n\t\t\tlog.NewHelper(log.WithContext(ctx, logger)).Log(level,\n\t\t\t\t\"kind\", \"server\",\n\t\t\t\t\"component\", kind,\n\t\t\t\t\"operation\", operation,\n\t\t\t\t\"args\", extractArgs(req),\n\t\t\t\t\"code\", code,\n\t\t\t\t\"reason\", reason,\n\t\t\t\t\"stack\", stack,\n\t\t\t\t\"latency\", time.Since(startTime).Seconds(),\n\t\t\t)\n\t\t\treturn\n\t\t}\n\t}\n}\n\n// Client is a client logging middleware.\nfunc Client(logger log.Logger) middleware.Middleware {\n\treturn func(handler middleware.Handler) middleware.Handler {\n\t\treturn func(ctx context.Context, req any) (reply any, err error) {\n\t\t\tvar (\n\t\t\t\tcode      int32\n\t\t\t\treason    string\n\t\t\t\tkind      string\n\t\t\t\toperation string\n\t\t\t)\n\n\t\t\t// default code\n\t\t\tcode = int32(status.FromGRPCCode(codes.OK))\n\n\t\t\tstartTime := time.Now()\n\t\t\tif info, ok := transport.FromClientContext(ctx); ok {\n\t\t\t\tkind = info.Kind().String()\n\t\t\t\toperation = info.Operation()\n\t\t\t}\n\t\t\treply, err = handler(ctx, req)\n\t\t\tif se := errors.FromError(err); se != nil {\n\t\t\t\tcode = se.Code\n\t\t\t\treason = se.Reason\n\t\t\t}\n\t\t\tlevel, stack := extractError(err)\n\t\t\tlog.NewHelper(log.WithContext(ctx, logger)).Log(level,\n\t\t\t\t\"kind\", \"client\",\n\t\t\t\t\"component\", kind,\n\t\t\t\t\"operation\", operation,\n\t\t\t\t\"args\", extractArgs(req),\n\t\t\t\t\"code\", code,\n\t\t\t\t\"reason\", reason,\n\t\t\t\t\"stack\", stack,\n\t\t\t\t\"latency\", time.Since(startTime).Seconds(),\n\t\t\t)\n\t\t\treturn\n\t\t}\n\t}\n}\n\n// extractArgs returns the string of the req\nfunc extractArgs(req any) string {\n\tif redacter, ok := req.(Redacter); ok {\n\t\treturn redacter.Redact()\n\t}\n\tif stringer, ok := req.(fmt.Stringer); ok {\n\t\treturn stringer.String()\n\t}\n\treturn fmt.Sprintf(\"%+v\", req)\n}\n\n// extractError returns the string of the error\nfunc extractError(err error) (log.Level, string) {\n\tif err != nil {\n\t\treturn log.LevelError, fmt.Sprintf(\"%+v\", err)\n\t}\n\treturn log.LevelInfo, \"\"\n}\n"
  },
  {
    "path": "middleware/logging/logging_test.go",
    "content": "package logging\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"errors\"\n\t\"testing\"\n\n\t\"github.com/go-kratos/kratos/v2/log\"\n\t\"github.com/go-kratos/kratos/v2/middleware\"\n\t\"github.com/go-kratos/kratos/v2/transport\"\n)\n\nvar _ transport.Transporter = (*Transport)(nil)\n\ntype Transport struct {\n\tkind      transport.Kind\n\tendpoint  string\n\toperation string\n}\n\nfunc (tr *Transport) Kind() transport.Kind {\n\treturn tr.kind\n}\n\nfunc (tr *Transport) Endpoint() string {\n\treturn tr.endpoint\n}\n\nfunc (tr *Transport) Operation() string {\n\treturn tr.operation\n}\n\nfunc (tr *Transport) RequestHeader() transport.Header {\n\treturn nil\n}\n\nfunc (tr *Transport) ReplyHeader() transport.Header {\n\treturn nil\n}\n\nfunc TestHTTP(t *testing.T) {\n\terr := errors.New(\"reply.error\")\n\tbf := bytes.NewBuffer(nil)\n\tlogger := log.NewStdLogger(bf)\n\n\ttests := []struct {\n\t\tname string\n\t\tkind func(logger log.Logger) middleware.Middleware\n\t\terr  error\n\t\tctx  context.Context\n\t}{\n\t\t{\n\t\t\t\"http-server@fail\",\n\t\t\tServer,\n\t\t\terr,\n\t\t\tfunc() context.Context {\n\t\t\t\treturn transport.NewServerContext(context.Background(), &Transport{kind: transport.KindHTTP, endpoint: \"endpoint\", operation: \"/package.service/method\"})\n\t\t\t}(),\n\t\t},\n\t\t{\n\t\t\t\"http-server@succ\",\n\t\t\tServer,\n\t\t\tnil,\n\t\t\tfunc() context.Context {\n\t\t\t\treturn transport.NewServerContext(context.Background(), &Transport{kind: transport.KindHTTP, endpoint: \"endpoint\", operation: \"/package.service/method\"})\n\t\t\t}(),\n\t\t},\n\t\t{\n\t\t\t\"http-client@succ\",\n\t\t\tClient,\n\t\t\tnil,\n\t\t\tfunc() context.Context {\n\t\t\t\treturn transport.NewClientContext(context.Background(), &Transport{kind: transport.KindHTTP, endpoint: \"endpoint\", operation: \"/package.service/method\"})\n\t\t\t}(),\n\t\t},\n\t\t{\n\t\t\t\"http-client@fail\",\n\t\t\tClient,\n\t\t\terr,\n\t\t\tfunc() context.Context {\n\t\t\t\treturn transport.NewClientContext(context.Background(), &Transport{kind: transport.KindHTTP, endpoint: \"endpoint\", operation: \"/package.service/method\"})\n\t\t\t}(),\n\t\t},\n\t}\n\n\tfor _, test := range tests {\n\t\tt.Run(test.name, func(t *testing.T) {\n\t\t\tbf.Reset()\n\t\t\tnext := func(context.Context, any) (any, error) {\n\t\t\t\treturn \"reply\", test.err\n\t\t\t}\n\t\t\tnext = test.kind(logger)(next)\n\t\t\tv, e := next(test.ctx, \"req.args\")\n\t\t\tt.Logf(\"[%s]reply: %v, error: %v\", test.name, v, e)\n\t\t\tt.Logf(\"[%s]log:%s\", test.name, bf.String())\n\t\t})\n\t}\n}\n\ntype (\n\tdummy struct {\n\t\tfield string\n\t}\n\tdummyStringer struct {\n\t\tfield string\n\t}\n\tdummyStringerRedacter struct {\n\t\tfield string\n\t}\n)\n\nfunc (d *dummyStringer) String() string {\n\treturn \"my value\"\n}\n\nfunc (d *dummyStringerRedacter) String() string {\n\treturn \"my value\"\n}\n\nfunc (d *dummyStringerRedacter) Redact() string {\n\treturn \"my value redacted\"\n}\n\nfunc TestExtractArgs(t *testing.T) {\n\ttests := []struct {\n\t\tname     string\n\t\treq      any\n\t\texpected string\n\t}{\n\t\t{\n\t\t\tname:     \"dummyStringer\",\n\t\t\treq:      &dummyStringer{field: \"\"},\n\t\t\texpected: \"my value\",\n\t\t}, {\n\t\t\tname:     \"dummy\",\n\t\t\treq:      &dummy{field: \"value\"},\n\t\t\texpected: \"&{field:value}\",\n\t\t}, {\n\t\t\tname:     \"dummyStringerRedacter\",\n\t\t\treq:      &dummyStringerRedacter{field: \"\"},\n\t\t\texpected: \"my value redacted\",\n\t\t},\n\t}\n\tfor _, test := range tests {\n\t\tt.Run(test.name, func(t *testing.T) {\n\t\t\tif value := extractArgs(test.req); value != test.expected {\n\t\t\t\tt.Errorf(`The stringified %s structure must be equal to \"%s\", %v given`, test.name, test.expected, value)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestExtractError(t *testing.T) {\n\ttests := []struct {\n\t\tname       string\n\t\terr        error\n\t\twantLevel  log.Level\n\t\twantErrStr string\n\t}{\n\t\t{\n\t\t\t\"no error\", nil, log.LevelInfo, \"\",\n\t\t},\n\t\t{\n\t\t\t\"error\", errors.New(\"test error\"), log.LevelError, \"test error\",\n\t\t},\n\t}\n\tfor _, test := range tests {\n\t\tt.Run(test.name, func(t *testing.T) {\n\t\t\tlevel, errStr := extractError(test.err)\n\t\t\tif level != test.wantLevel {\n\t\t\t\tt.Errorf(\"want: %d, got: %d\", test.wantLevel, level)\n\t\t\t}\n\t\t\tif errStr != test.wantErrStr {\n\t\t\t\tt.Errorf(\"want: %s, got: %s\", test.wantErrStr, errStr)\n\t\t\t}\n\t\t})\n\t}\n}\n\ntype extractKeyValues [][]any\n\nfunc (l *extractKeyValues) Log(_ log.Level, kv ...any) error { *l = append(*l, kv); return nil }\n\nfunc TestServer_CallerPath(t *testing.T) {\n\tvar a extractKeyValues\n\tlogger := log.With(&a, \"caller\", log.Caller(5)) // report where the helper was called\n\n\t// make sure the caller is same\n\tsameCaller := func(fn middleware.Handler) { _, _ = fn(context.Background(), nil) }\n\n\t// caller: [... log inside middleware, fn(context.Background(), nil)]\n\th := func(context.Context, any) (a any, e error) { return }\n\th = Server(logger)(h)\n\tsameCaller(h)\n\n\t// caller: [... helper.Info(\"foo\"), fn(context.Background(), nil)]\n\thelper := log.NewHelper(logger)\n\tsameCaller(func(context.Context, any) (a any, e error) { helper.Info(\"foo\"); return })\n\n\tt.Log(a[0])\n\tt.Log(a[1])\n\tif a[0][0] != \"caller\" || a[1][0] != \"caller\" {\n\t\tt.Fatal(\"caller not found\")\n\t}\n\tif a[0][1] != a[1][1] {\n\t\tt.Fatalf(\"middleware should have the same caller as log.Helper. middleware: %s, helper: %s\", a[0][1], a[1][1])\n\t}\n}\n\nfunc TestClient_CallerPath(t *testing.T) {\n\tvar a extractKeyValues\n\tlogger := log.With(&a, \"caller\", log.Caller(5)) // report where the helper was called\n\n\t// make sure the caller is same\n\tsameCaller := func(fn middleware.Handler) { _, _ = fn(context.Background(), nil) }\n\n\t// caller: [... log inside middleware, fn(context.Background(), nil)]\n\th := func(context.Context, any) (a any, e error) { return }\n\th = Client(logger)(h)\n\tsameCaller(h)\n\n\t// caller: [... helper.Info(\"foo\"), fn(context.Background(), nil)]\n\thelper := log.NewHelper(logger)\n\tsameCaller(func(context.Context, any) (a any, e error) { helper.Info(\"foo\"); return })\n\n\tt.Log(a[0])\n\tt.Log(a[1])\n\tif a[0][0] != \"caller\" || a[1][0] != \"caller\" {\n\t\tt.Fatal(\"caller not found\")\n\t}\n\tif a[0][1] != a[1][1] {\n\t\tt.Fatalf(\"middleware should have the same caller as log.Helper. middleware: %s, helper: %s\", a[0][1], a[1][1])\n\t}\n}\n"
  },
  {
    "path": "middleware/metadata/metadata.go",
    "content": "package metadata\n\nimport (\n\t\"context\"\n\t\"strings\"\n\n\t\"github.com/go-kratos/kratos/v2/metadata\"\n\t\"github.com/go-kratos/kratos/v2/middleware\"\n\t\"github.com/go-kratos/kratos/v2/transport\"\n)\n\n// Option is metadata option.\ntype Option func(*options)\n\ntype options struct {\n\tprefix []string\n\tmd     metadata.Metadata\n}\n\nfunc (o *options) hasPrefix(key string) bool {\n\tk := strings.ToLower(key)\n\tfor _, prefix := range o.prefix {\n\t\tif strings.HasPrefix(k, prefix) {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\n// WithConstants with constant metadata key value.\nfunc WithConstants(md metadata.Metadata) Option {\n\treturn func(o *options) {\n\t\to.md = md\n\t}\n}\n\n// WithPropagatedPrefix with propagated key prefix.\nfunc WithPropagatedPrefix(prefix ...string) Option {\n\treturn func(o *options) {\n\t\to.prefix = prefix\n\t}\n}\n\n// Server is middleware server-side metadata.\nfunc Server(opts ...Option) middleware.Middleware {\n\toptions := &options{\n\t\tprefix: []string{\"x-md-\"}, // x-md-global-, x-md-local\n\t}\n\tfor _, o := range opts {\n\t\to(options)\n\t}\n\treturn func(handler middleware.Handler) middleware.Handler {\n\t\treturn func(ctx context.Context, req any) (reply any, err error) {\n\t\t\ttr, ok := transport.FromServerContext(ctx)\n\t\t\tif !ok {\n\t\t\t\treturn handler(ctx, req)\n\t\t\t}\n\n\t\t\tmd := options.md.Clone()\n\t\t\theader := tr.RequestHeader()\n\t\t\tfor _, k := range header.Keys() {\n\t\t\t\tif options.hasPrefix(k) {\n\t\t\t\t\tfor _, v := range header.Values(k) {\n\t\t\t\t\t\tmd.Add(k, v)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tctx = metadata.NewServerContext(ctx, md)\n\t\t\treturn handler(ctx, req)\n\t\t}\n\t}\n}\n\n// Client is middleware client-side metadata.\nfunc Client(opts ...Option) middleware.Middleware {\n\toptions := &options{\n\t\tprefix: []string{\"x-md-global-\"},\n\t}\n\tfor _, o := range opts {\n\t\to(options)\n\t}\n\treturn func(handler middleware.Handler) middleware.Handler {\n\t\treturn func(ctx context.Context, req any) (reply any, err error) {\n\t\t\ttr, ok := transport.FromClientContext(ctx)\n\t\t\tif !ok {\n\t\t\t\treturn handler(ctx, req)\n\t\t\t}\n\n\t\t\theader := tr.RequestHeader()\n\t\t\t// x-md-local-\n\t\t\tfor k, vList := range options.md {\n\t\t\t\tfor _, v := range vList {\n\t\t\t\t\theader.Add(k, v)\n\t\t\t\t}\n\t\t\t}\n\t\t\tif md, ok := metadata.FromClientContext(ctx); ok {\n\t\t\t\tfor k, vList := range md {\n\t\t\t\t\tfor _, v := range vList {\n\t\t\t\t\t\theader.Add(k, v)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\t// x-md-global-\n\t\t\tif md, ok := metadata.FromServerContext(ctx); ok {\n\t\t\t\tfor k, vList := range md {\n\t\t\t\t\tif options.hasPrefix(k) {\n\t\t\t\t\t\tfor _, v := range vList {\n\t\t\t\t\t\t\theader.Add(k, v)\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn handler(ctx, req)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "middleware/metadata/metadata_test.go",
    "content": "package metadata\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"net/http\"\n\t\"reflect\"\n\t\"testing\"\n\n\t\"github.com/go-kratos/kratos/v2/metadata\"\n\t\"github.com/go-kratos/kratos/v2/transport\"\n)\n\ntype headerCarrier http.Header\n\nfunc (hc headerCarrier) Get(key string) string { return http.Header(hc).Get(key) }\n\nfunc (hc headerCarrier) Set(key string, value string) { http.Header(hc).Set(key, value) }\n\nfunc (hc headerCarrier) Add(key string, value string) { http.Header(hc).Add(key, value) }\n\n// Keys lists the keys stored in this carrier.\nfunc (hc headerCarrier) Keys() []string {\n\tkeys := make([]string, 0, len(hc))\n\tfor k := range http.Header(hc) {\n\t\tkeys = append(keys, k)\n\t}\n\treturn keys\n}\n\n// Values returns a slice value associated with the passed key.\nfunc (hc headerCarrier) Values(key string) []string {\n\treturn http.Header(hc).Values(key)\n}\n\ntype testTransport struct{ header headerCarrier }\n\nfunc (tr *testTransport) Kind() transport.Kind            { return transport.KindHTTP }\nfunc (tr *testTransport) Endpoint() string                { return \"\" }\nfunc (tr *testTransport) Operation() string               { return \"\" }\nfunc (tr *testTransport) RequestHeader() transport.Header { return tr.header }\nfunc (tr *testTransport) ReplyHeader() transport.Header   { return tr.header }\n\nvar (\n\tglobalKey   = \"x-md-global-key\"\n\tglobalValue = \"global-value\"\n\tlocalKey    = \"x-md-local-key\"\n\tlocalValue  = \"local-value\"\n\tcustomKey   = \"x-md-local-custom\"\n\tcustomValue = \"custom-value\"\n\tconstKey    = \"x-md-local-const\"\n\tconstValue  = \"x-md-local-const\"\n)\n\nfunc TestSever(t *testing.T) {\n\ths := func(ctx context.Context, in any) (any, error) {\n\t\tmd, ok := metadata.FromServerContext(ctx)\n\t\tif !ok {\n\t\t\treturn nil, errors.New(\"no md\")\n\t\t}\n\t\tif md.Get(constKey) != constValue {\n\t\t\treturn nil, errors.New(\"const not equal\")\n\t\t}\n\t\tif md.Get(globalKey) != globalValue {\n\t\t\treturn nil, errors.New(\"global not equal\")\n\t\t}\n\t\tif md.Get(localKey) != localValue {\n\t\t\treturn nil, errors.New(\"local not equal\")\n\t\t}\n\t\treturn in, nil\n\t}\n\thc := headerCarrier{}\n\thc.Set(globalKey, globalValue)\n\thc.Set(localKey, localValue)\n\tctx := transport.NewServerContext(context.Background(), &testTransport{hc})\n\t// const md\n\tconstMD := metadata.New()\n\tconstMD.Set(constKey, constValue)\n\treply, err := Server(WithConstants(constMD))(hs)(ctx, \"foo\")\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif reply.(string) != \"foo\" {\n\t\tt.Fatalf(\"want foo got %v\", reply)\n\t}\n}\n\nfunc TestClient(t *testing.T) {\n\ths := func(ctx context.Context, in any) (any, error) {\n\t\ttr, ok := transport.FromClientContext(ctx)\n\t\tif !ok {\n\t\t\treturn nil, errors.New(\"no md\")\n\t\t}\n\t\tif tr.RequestHeader().Get(constKey) != constValue {\n\t\t\treturn nil, errors.New(\"const not equal\")\n\t\t}\n\t\tif tr.RequestHeader().Get(customKey) != customValue {\n\t\t\treturn nil, errors.New(\"custom not equal\")\n\t\t}\n\t\tif tr.RequestHeader().Get(globalKey) != globalValue {\n\t\t\treturn nil, errors.New(\"global not equal\")\n\t\t}\n\t\tif tr.RequestHeader().Get(localKey) != \"\" {\n\t\t\treturn nil, errors.New(\"local must empty\")\n\t\t}\n\t\treturn in, nil\n\t}\n\t// server md\n\tserverMD := metadata.New()\n\tserverMD.Set(globalKey, globalValue)\n\tserverMD.Set(localKey, localValue)\n\tctx := metadata.NewServerContext(context.Background(), serverMD)\n\t// client md\n\tclientMD := metadata.New()\n\tclientMD.Set(customKey, customValue)\n\tctx = metadata.NewClientContext(ctx, clientMD)\n\t// transport carrier\n\tctx = transport.NewClientContext(ctx, &testTransport{headerCarrier{}})\n\t// const md\n\tconstMD := metadata.New()\n\tconstMD.Set(constKey, constValue)\n\treply, err := Client(WithConstants(constMD))(hs)(ctx, \"bar\")\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif reply.(string) != \"bar\" {\n\t\tt.Fatalf(\"want foo got %v\", reply)\n\t}\n}\n\nfunc TestWithConstants(t *testing.T) {\n\tmd := metadata.Metadata{\n\t\tconstKey: {constValue},\n\t}\n\toptions := &options{\n\t\tmd: metadata.Metadata{\n\t\t\t\"override\": {\"override\"},\n\t\t},\n\t}\n\n\tWithConstants(md)(options)\n\tif !reflect.DeepEqual(md, options.md) {\n\t\tt.Errorf(\"want: %v, got: %v\", md, options.md)\n\t}\n}\n\nfunc TestOptions_WithPropagatedPrefix(t *testing.T) {\n\toptions := &options{\n\t\tprefix: []string{\"override\"},\n\t}\n\tprefixes := []string{\"something\", \"another\"}\n\n\tWithPropagatedPrefix(prefixes...)(options)\n\tif !reflect.DeepEqual(prefixes, options.prefix) {\n\t\tt.Error(\"The prefix must be overridden.\")\n\t}\n}\n\nfunc TestOptions_hasPrefix(t *testing.T) {\n\ttests := []struct {\n\t\tname    string\n\t\toptions *options\n\t\tkey     string\n\t\texists  bool\n\t}{\n\t\t{\"exists key upper\", &options{prefix: []string{\"prefix\"}}, \"PREFIX_true\", true},\n\t\t{\"exists key lower\", &options{prefix: []string{\"prefix\"}}, \"prefix_true\", true},\n\t\t{\"not exists key upper\", &options{prefix: []string{\"prefix\"}}, \"false_PREFIX\", false},\n\t\t{\"not exists key lower\", &options{prefix: []string{\"prefix\"}}, \"false_prefix\", false},\n\t}\n\tfor _, test := range tests {\n\t\tt.Run(test.name, func(t *testing.T) {\n\t\t\texists := test.options.hasPrefix(test.key)\n\t\t\tif test.exists != exists {\n\t\t\t\tt.Errorf(\"key: '%sr', not exists prefixs: %v\", test.key, test.options.prefix)\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "middleware/metrics/metrics.go",
    "content": "package metrics\n\nimport (\n\t\"context\"\n\t\"time\"\n\n\t\"go.opentelemetry.io/otel/attribute\"\n\t\"go.opentelemetry.io/otel/metric\"\n\tmetricsdk \"go.opentelemetry.io/otel/sdk/metric\"\n\t\"google.golang.org/grpc/codes\"\n\n\t\"github.com/go-kratos/kratos/v2/errors\"\n\t\"github.com/go-kratos/kratos/v2/middleware\"\n\t\"github.com/go-kratos/kratos/v2/transport\"\n\t\"github.com/go-kratos/kratos/v2/transport/http/status\"\n)\n\nconst (\n\tmetricLabelKind      = \"kind\"\n\tmetricLabelOperation = \"operation\"\n\tmetricLabelCode      = \"code\"\n\tmetricLabelReason    = \"reason\"\n)\n\nconst (\n\tDefaultServerSecondsHistogramName = \"server_requests_seconds\"\n\tDefaultServerRequestsCounterName  = \"server_requests_code_total\"\n\tDefaultClientSecondsHistogramName = \"client_requests_seconds\"\n\tDefaultClientRequestsCounterName  = \"client_requests_code_total\"\n)\n\n// Option is metrics option.\ntype Option func(*options)\n\n// WithRequests with requests counter.\nfunc WithRequests(c metric.Int64Counter) Option {\n\treturn func(o *options) {\n\t\to.requests = c\n\t}\n}\n\n// WithSeconds with seconds histogram.\n// notice: the record unit in current middleware is s(Seconds)\nfunc WithSeconds(histogram metric.Float64Histogram) Option {\n\treturn func(o *options) {\n\t\to.seconds = histogram\n\t}\n}\n\n// DefaultRequestsCounter\n// return metric.Int64Counter for WithRequests\n// suggest histogramName = <client/server>_requests_code_total\nfunc DefaultRequestsCounter(meter metric.Meter, histogramName string) (metric.Int64Counter, error) {\n\treturn meter.Int64Counter(histogramName, metric.WithUnit(\"{call}\"))\n}\n\n// DefaultSecondsHistogram\n// return metric.Float64Histogram for WithSeconds\n// suggest histogramName = <client/server>_requests_seconds\nfunc DefaultSecondsHistogram(meter metric.Meter, histogramName string) (metric.Float64Histogram, error) {\n\treturn meter.Float64Histogram(\n\t\thistogramName,\n\t\tmetric.WithUnit(\"s\"),\n\t\tmetric.WithExplicitBucketBoundaries(0.005, 0.01, 0.025, 0.05, 0.1, 0.250, 0.5, 1),\n\t)\n}\n\n// DefaultSecondsHistogramView\n// need register in sdkmetric.MeterProvider\n// eg:\n// view := SecondsHistogramView()\n// mp := sdkmetric.NewMeterProvider(sdkmetric.WithView(view))\n// otel.SetMeterProvider(mp)\nfunc DefaultSecondsHistogramView(histogramName string) metricsdk.View {\n\treturn func(instrument metricsdk.Instrument) (metricsdk.Stream, bool) {\n\t\tif instrument.Name == histogramName {\n\t\t\treturn metricsdk.Stream{\n\t\t\t\tName:        instrument.Name,\n\t\t\t\tDescription: instrument.Description,\n\t\t\t\tUnit:        instrument.Unit,\n\t\t\t\tAggregation: metricsdk.AggregationExplicitBucketHistogram{\n\t\t\t\t\tBoundaries: []float64{0.005, 0.01, 0.025, 0.05, 0.1, 0.250, 0.5, 1},\n\t\t\t\t\tNoMinMax:   true,\n\t\t\t\t},\n\t\t\t\tAttributeFilter: func(attribute.KeyValue) bool {\n\t\t\t\t\treturn true\n\t\t\t\t},\n\t\t\t}, true\n\t\t}\n\t\treturn metricsdk.Stream{}, false\n\t}\n}\n\ntype options struct {\n\t// counter: <client/server>_requests_code_total{kind, operation, code, reason}\n\trequests metric.Int64Counter\n\t// histogram: <client/server>_requests_seconds{kind, operation}\n\tseconds metric.Float64Histogram\n}\n\n// Server is middleware server-side metrics.\nfunc Server(opts ...Option) middleware.Middleware {\n\top := options{}\n\tfor _, o := range opts {\n\t\to(&op)\n\t}\n\treturn func(handler middleware.Handler) middleware.Handler {\n\t\treturn func(ctx context.Context, req any) (any, error) {\n\t\t\t// if requests and seconds are nil, return directly\n\t\t\tif op.requests == nil && op.seconds == nil {\n\t\t\t\treturn handler(ctx, req)\n\t\t\t}\n\n\t\t\tvar (\n\t\t\t\tcode      int\n\t\t\t\treason    string\n\t\t\t\tkind      string\n\t\t\t\toperation string\n\t\t\t)\n\n\t\t\t// default code\n\t\t\tcode = status.FromGRPCCode(codes.OK)\n\n\t\t\tstartTime := time.Now()\n\t\t\tif info, ok := transport.FromServerContext(ctx); ok {\n\t\t\t\tkind = info.Kind().String()\n\t\t\t\toperation = info.Operation()\n\t\t\t}\n\t\t\treply, err := handler(ctx, req)\n\t\t\tif se := errors.FromError(err); se != nil {\n\t\t\t\tcode = int(se.Code)\n\t\t\t\treason = se.Reason\n\t\t\t}\n\t\t\tif op.requests != nil {\n\t\t\t\top.requests.Add(\n\t\t\t\t\tctx, 1,\n\t\t\t\t\tmetric.WithAttributes(\n\t\t\t\t\t\tattribute.String(metricLabelKind, kind),\n\t\t\t\t\t\tattribute.String(metricLabelOperation, operation),\n\t\t\t\t\t\tattribute.Int(metricLabelCode, code),\n\t\t\t\t\t\tattribute.String(metricLabelReason, reason),\n\t\t\t\t\t),\n\t\t\t\t)\n\t\t\t}\n\t\t\tif op.seconds != nil {\n\t\t\t\top.seconds.Record(\n\t\t\t\t\tctx, time.Since(startTime).Seconds(),\n\t\t\t\t\tmetric.WithAttributes(\n\t\t\t\t\t\tattribute.String(metricLabelKind, kind),\n\t\t\t\t\t\tattribute.String(metricLabelOperation, operation),\n\t\t\t\t\t),\n\t\t\t\t)\n\t\t\t}\n\t\t\treturn reply, err\n\t\t}\n\t}\n}\n\n// Client is middleware client-side metrics.\nfunc Client(opts ...Option) middleware.Middleware {\n\top := options{}\n\tfor _, o := range opts {\n\t\to(&op)\n\t}\n\treturn func(handler middleware.Handler) middleware.Handler {\n\t\treturn func(ctx context.Context, req any) (any, error) {\n\t\t\tvar (\n\t\t\t\tcode      int\n\t\t\t\treason    string\n\t\t\t\tkind      string\n\t\t\t\toperation string\n\t\t\t)\n\n\t\t\t// default code\n\t\t\tcode = status.FromGRPCCode(codes.OK)\n\n\t\t\tstartTime := time.Now()\n\t\t\tif info, ok := transport.FromClientContext(ctx); ok {\n\t\t\t\tkind = info.Kind().String()\n\t\t\t\toperation = info.Operation()\n\t\t\t}\n\t\t\treply, err := handler(ctx, req)\n\t\t\tif se := errors.FromError(err); se != nil {\n\t\t\t\tcode = int(se.Code)\n\t\t\t\treason = se.Reason\n\t\t\t}\n\t\t\tif op.requests != nil {\n\t\t\t\top.requests.Add(\n\t\t\t\t\tctx, 1,\n\t\t\t\t\tmetric.WithAttributes(\n\t\t\t\t\t\tattribute.String(metricLabelKind, kind),\n\t\t\t\t\t\tattribute.String(metricLabelOperation, operation),\n\t\t\t\t\t\tattribute.Int(metricLabelCode, code),\n\t\t\t\t\t\tattribute.String(metricLabelReason, reason),\n\t\t\t\t\t),\n\t\t\t\t)\n\t\t\t}\n\t\t\tif op.seconds != nil {\n\t\t\t\top.seconds.Record(\n\t\t\t\t\tctx, time.Since(startTime).Seconds(),\n\t\t\t\t\tmetric.WithAttributes(\n\t\t\t\t\t\tattribute.String(metricLabelKind, kind),\n\t\t\t\t\t\tattribute.String(metricLabelOperation, operation),\n\t\t\t\t\t),\n\t\t\t\t)\n\t\t\t}\n\t\t\treturn reply, err\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "middleware/metrics/metrics_test.go",
    "content": "package metrics\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"math/rand/v2\"\n\t\"strings\"\n\t\"sync\"\n\t\"testing\"\n\t\"time\"\n\n\t\"go.opentelemetry.io/otel\"\n\t\"go.opentelemetry.io/otel/attribute\"\n\tsdkmetric \"go.opentelemetry.io/otel/sdk/metric\"\n\t\"go.opentelemetry.io/otel/sdk/metric/metricdata\"\n\tsdktrace \"go.opentelemetry.io/otel/sdk/trace\"\n\n\t\"github.com/go-kratos/kratos/v2/transport\"\n\t\"github.com/go-kratos/kratos/v2/transport/http\"\n)\n\ntype dummyExporter struct {\n\tmu       sync.Mutex\n\twriteBuf bytes.Buffer\n}\n\nfunc (x *dummyExporter) Temporality(kind sdkmetric.InstrumentKind) metricdata.Temporality {\n\treturn sdkmetric.DefaultTemporalitySelector(kind)\n}\n\nfunc (x *dummyExporter) Aggregation(kind sdkmetric.InstrumentKind) sdkmetric.Aggregation {\n\treturn sdkmetric.DefaultAggregationSelector(kind)\n}\n\nfunc (x *dummyExporter) Export(ctx context.Context, resourceMetrics *metricdata.ResourceMetrics) error {\n\tselect {\n\tcase <-ctx.Done():\n\t\t// Don't do anything if the context has already timed out.\n\t\treturn ctx.Err()\n\tdefault:\n\t\t// Context is still valid, continue.\n\t}\n\tx.mu.Lock()\n\tdefer x.mu.Unlock()\n\treturn json.NewEncoder(&x.writeBuf).Encode(resourceMetrics)\n}\n\nfunc (x *dummyExporter) ForceFlush(ctx context.Context) error {\n\treturn ctx.Err()\n}\n\nfunc (x *dummyExporter) Shutdown(ctx context.Context) error {\n\treturn ctx.Err()\n}\n\nfunc (x *dummyExporter) String() string {\n\tx.mu.Lock()\n\tdefer x.mu.Unlock()\n\treturn x.writeBuf.String()\n}\n\nvar exporter = &dummyExporter{}\n\nfunc init() {\n\terr := EnableOTELExemplar()\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tmp := sdkmetric.NewMeterProvider(\n\t\tsdkmetric.WithReader(sdkmetric.NewPeriodicReader(exporter, sdkmetric.WithInterval(time.Microsecond*400))),\n\t\tsdkmetric.WithView(DefaultSecondsHistogramView(DefaultServerSecondsHistogramName), func(instrument sdkmetric.Instrument) (sdkmetric.Stream, bool) {\n\t\t\treturn sdkmetric.Stream{\n\t\t\t\tName:        instrument.Name,\n\t\t\t\tDescription: instrument.Description,\n\t\t\t\tUnit:        instrument.Unit,\n\t\t\t\tAttributeFilter: func(attribute.KeyValue) bool {\n\t\t\t\t\treturn true\n\t\t\t\t},\n\t\t\t}, true\n\t\t}),\n\t)\n\totel.SetMeterProvider(mp)\n\n\ttr := sdktrace.NewTracerProvider()\n\totel.SetTracerProvider(tr)\n}\n\nfunc TestWithRequests(t *testing.T) {\n\to := options{}\n\n\tif o.requests != nil {\n\t\tt.Errorf(`The type of the option requests property must be of \"nil\"`)\n\t\treturn\n\t}\n\n\tmeter := otel.Meter(\"test_meter\")\n\trequests, err := meter.Int64Counter(DefaultServerRequestsCounterName)\n\tif err != nil {\n\t\tt.Errorf(\"[Int64Counter] something went wrong: %v\", err)\n\t\treturn\n\t}\n\n\tWithRequests(requests)(&o)\n\n\tif o.requests == nil {\n\t\tt.Errorf(`The type of the option requests property must be of \"mockCounter\", %T given.`, o.requests)\n\t}\n}\n\nfunc TestWithSeconds(t *testing.T) {\n\to := options{}\n\n\tif o.seconds != nil {\n\t\tt.Errorf(`The type of the option seconds property must be of \"nil\"`)\n\t\treturn\n\t}\n\n\tmeter := otel.Meter(\"test_meter\")\n\tseconds, err := meter.Float64Histogram(DefaultServerSecondsHistogramName)\n\tif err != nil {\n\t\tt.Errorf(\"[Float64Histogram] something went wrong: %v\", err)\n\t\treturn\n\t}\n\n\tWithSeconds(seconds)(&o)\n\n\tif o.seconds == nil {\n\t\tt.Errorf(`The type of the option seconds property must be of \"mockObserver\", %T given.`, o.requests)\n\t}\n}\n\nfunc TestServer(t *testing.T) {\n\ttracer := otel.Tracer(\"test_trace\")\n\tctx, span := tracer.Start(context.Background(), \"TestServer\")\n\tdefer span.End()\n\n\te := errors.New(\"got an error\")\n\tnextError := func(context.Context, any) (any, error) {\n\t\treturn nil, e\n\t}\n\tnextValid := func(context.Context, any) (any, error) {\n\t\ttime.Sleep(time.Millisecond * time.Duration(rand.Int32N(100)))\n\t\treturn \"Hello valid\", nil\n\t}\n\n\t// init server handler\n\tmeter := otel.Meter(\"test_meter\")\n\trequests, err := DefaultRequestsCounter(meter, DefaultServerRequestsCounterName)\n\tif err != nil {\n\t\tt.Errorf(\"[DefaultRequestsCounter] something went wrong: %v\", err)\n\t\treturn\n\t}\n\tseconds, err := DefaultSecondsHistogram(meter, DefaultServerSecondsHistogramName)\n\tif err != nil {\n\t\tt.Errorf(\"[DefaultSecondsHistogram] something went wrong: %v\", err)\n\t\treturn\n\t}\n\tserverHandler := Server(\n\t\tWithRequests(requests),\n\t\tWithSeconds(seconds),\n\t)\n\n\t_, err = serverHandler(nextError)(ctx, \"test:\")\n\tif err != e {\n\t\tt.Error(\"The given error mismatch the expected.\")\n\t\treturn\n\t}\n\n\tfor i := 0; i < 20; i++ {\n\t\tres, err := serverHandler(nextValid)(transport.NewServerContext(ctx, &http.Transport{}), \"test:\")\n\t\tif err != nil {\n\t\t\tt.Error(\"The server must not throw an error.\")\n\t\t}\n\t\tif res != \"Hello valid\" {\n\t\t\tt.Error(`The server must return a \"Hello valid\" response.`)\n\t\t}\n\t}\n\tbufStr := exporter.String()\n\n\tfor _, label := range []string{\n\t\tmetricLabelKind,\n\t\tmetricLabelOperation,\n\t\tmetricLabelCode,\n\t\tmetricLabelReason,\n\t\t\"SpanID\",\n\t\t\"TraceID\",\n\t} {\n\t\tif !strings.Contains(bufStr, fmt.Sprintf(\"\\\"%s\\\"\", label)) {\n\t\t\tt.Errorf(\"The expected metric label %s is not found in the output: %s\", label, bufStr)\n\t\t}\n\t}\n}\n\nfunc TestClient(t *testing.T) {\n\ttracer := otel.Tracer(\"test_trace\")\n\tctx, span := tracer.Start(context.Background(), \"TestClient\")\n\tdefer span.End()\n\n\te := errors.New(\"got an error\")\n\tnextError := func(context.Context, any) (any, error) {\n\t\treturn nil, e\n\t}\n\tnextValid := func(context.Context, any) (any, error) {\n\t\treturn \"Hello valid\", nil\n\t}\n\n\t// init client handler\n\tmeter := otel.Meter(\"test_meter\")\n\trequests, err := DefaultRequestsCounter(meter, DefaultServerRequestsCounterName)\n\tif err != nil {\n\t\tt.Errorf(\"[DefaultRequestsCounter] something went wrong: %v\", err)\n\t\treturn\n\t}\n\tseconds, err := DefaultSecondsHistogram(meter, DefaultServerSecondsHistogramName)\n\tif err != nil {\n\t\tt.Errorf(\"[DefaultSecondsHistogram] something went wrong: %v\", err)\n\t\treturn\n\t}\n\tclientHandler := Client(\n\t\tWithRequests(requests),\n\t\tWithSeconds(seconds),\n\t)\n\n\t_, err = clientHandler(nextError)(ctx, \"test:\")\n\tif err != e {\n\t\tt.Error(\"The given error mismatch the expected.\")\n\t}\n\n\tfor i := 0; i < 20; i++ {\n\t\tres, err := clientHandler(nextValid)(transport.NewClientContext(ctx, &http.Transport{}), \"test:\")\n\t\tif err != nil {\n\t\t\tt.Error(\"The server must not throw an error.\")\n\t\t}\n\t\tif res != \"Hello valid\" {\n\t\t\tt.Error(`The server must return a \"Hello valid\" response.`)\n\t\t}\n\t}\n\tbufStr := exporter.String()\n\n\tfor _, label := range []string{\n\t\tmetricLabelKind,\n\t\tmetricLabelOperation,\n\t\tmetricLabelCode,\n\t\tmetricLabelReason,\n\t\t\"SpanID\",\n\t\t\"TraceID\",\n\t} {\n\t\tif !strings.Contains(bufStr, fmt.Sprintf(\"\\\"%s\\\"\", label)) {\n\t\t\tt.Errorf(\"The expected metric label %s is not found in the output: %s\", label, bufStr)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "middleware/metrics/otel.go",
    "content": "package metrics\n\nimport \"os\"\n\nfunc EnableOTELExemplar() error {\n\treturn os.Setenv(\"OTEL_GO_X_EXEMPLAR\", \"true\")\n}\n"
  },
  {
    "path": "middleware/middleware.go",
    "content": "package middleware\n\nimport (\n\t\"context\"\n)\n\n// Handler defines the handler invoked by Middleware.\ntype Handler func(ctx context.Context, req any) (any, error)\n\n// Middleware is HTTP/gRPC transport middleware.\ntype Middleware func(Handler) Handler\n\n// Chain returns a Middleware that specifies the chained handler for endpoint.\nfunc Chain(m ...Middleware) Middleware {\n\treturn func(next Handler) Handler {\n\t\tfor i := len(m) - 1; i >= 0; i-- {\n\t\t\tnext = m[i](next)\n\t\t}\n\t\treturn next\n\t}\n}\n"
  },
  {
    "path": "middleware/middleware_test.go",
    "content": "package middleware\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"reflect\"\n\t\"testing\"\n)\n\nvar i int\n\nfunc TestChain(t *testing.T) {\n\tnext := func(_ context.Context, req any) (any, error) {\n\t\tif req != \"hello kratos!\" {\n\t\t\tt.Errorf(\"expect %v, got %v\", \"hello kratos!\", req)\n\t\t}\n\t\ti += 10\n\t\treturn \"reply\", nil\n\t}\n\n\tgot, err := Chain(test1Middleware, test2Middleware, test3Middleware)(next)(context.Background(), \"hello kratos!\")\n\tif err != nil {\n\t\tt.Errorf(\"expect %v, got %v\", nil, err)\n\t}\n\tif !reflect.DeepEqual(got, \"reply\") {\n\t\tt.Errorf(\"expect %v, got %v\", \"reply\", got)\n\t}\n\tif !reflect.DeepEqual(i, 16) {\n\t\tt.Errorf(\"expect %v, got %v\", 16, i)\n\t}\n}\n\nfunc test1Middleware(handler Handler) Handler {\n\treturn func(ctx context.Context, req any) (reply any, err error) {\n\t\tfmt.Println(\"test1 before\")\n\t\ti++\n\t\treply, err = handler(ctx, req)\n\t\tfmt.Println(\"test1 after\")\n\t\treturn\n\t}\n}\n\nfunc test2Middleware(handler Handler) Handler {\n\treturn func(ctx context.Context, req any) (reply any, err error) {\n\t\tfmt.Println(\"test2 before\")\n\t\ti += 2\n\t\treply, err = handler(ctx, req)\n\t\tfmt.Println(\"test2 after\")\n\t\treturn\n\t}\n}\n\nfunc test3Middleware(handler Handler) Handler {\n\treturn func(ctx context.Context, req any) (reply any, err error) {\n\t\tfmt.Println(\"test3 before\")\n\t\ti += 3\n\t\treply, err = handler(ctx, req)\n\t\tfmt.Println(\"test3 after\")\n\t\treturn\n\t}\n}\n"
  },
  {
    "path": "middleware/ratelimit/ratelimit.go",
    "content": "package ratelimit\n\nimport (\n\t\"context\"\n\n\t\"github.com/go-kratos/aegis/ratelimit\"\n\t\"github.com/go-kratos/aegis/ratelimit/bbr\"\n\n\t\"github.com/go-kratos/kratos/v2/errors\"\n\t\"github.com/go-kratos/kratos/v2/middleware\"\n)\n\n// ErrLimitExceed is service unavailable due to rate limit exceeded.\nvar ErrLimitExceed = errors.New(429, \"RATELIMIT\", \"service unavailable due to rate limit exceeded\")\n\n// Option is ratelimit option.\ntype Option func(*options)\n\n// WithLimiter set Limiter implementation,\n// default is bbr limiter\nfunc WithLimiter(limiter ratelimit.Limiter) Option {\n\treturn func(o *options) {\n\t\to.limiter = limiter\n\t}\n}\n\ntype options struct {\n\tlimiter ratelimit.Limiter\n}\n\n// Server ratelimiter middleware\nfunc Server(opts ...Option) middleware.Middleware {\n\toptions := &options{\n\t\tlimiter: bbr.NewLimiter(),\n\t}\n\tfor _, o := range opts {\n\t\to(options)\n\t}\n\treturn func(handler middleware.Handler) middleware.Handler {\n\t\treturn func(ctx context.Context, req any) (reply any, err error) {\n\t\t\tdone, e := options.limiter.Allow()\n\t\t\tif e != nil {\n\t\t\t\t// rejected\n\t\t\t\treturn nil, ErrLimitExceed\n\t\t\t}\n\t\t\t// allowed\n\t\t\treply, err = handler(ctx, req)\n\t\t\tdone(ratelimit.DoneInfo{Err: err})\n\t\t\treturn\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "middleware/ratelimit/ratelimit_test.go",
    "content": "package ratelimit\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"testing\"\n\n\t\"github.com/go-kratos/aegis/ratelimit\"\n)\n\ntype (\n\tratelimitMock struct {\n\t\treached bool\n\t}\n\tratelimitReachedMock struct {\n\t\treached bool\n\t}\n)\n\nfunc (r *ratelimitMock) Allow() (ratelimit.DoneFunc, error) {\n\treturn func(_ ratelimit.DoneInfo) {\n\t\tr.reached = true\n\t}, nil\n}\n\nfunc (r *ratelimitReachedMock) Allow() (ratelimit.DoneFunc, error) {\n\treturn func(_ ratelimit.DoneInfo) {\n\t\tr.reached = true\n\t}, errors.New(\"errored\")\n}\n\nfunc Test_WithLimiter(t *testing.T) {\n\to := options{\n\t\tlimiter: &ratelimitMock{},\n\t}\n\n\tWithLimiter(nil)(&o)\n\tif o.limiter != nil {\n\t\tt.Error(\"The limiter property must be updated.\")\n\t}\n}\n\nfunc TestServer(t *testing.T) {\n\tnextValid := func(context.Context, any) (any, error) {\n\t\treturn \"Hello valid\", nil\n\t}\n\n\trlm := &ratelimitMock{}\n\trlrm := &ratelimitReachedMock{}\n\n\t_, _ = Server(func(o *options) {\n\t\to.limiter = rlm\n\t})(nextValid)(context.Background(), nil)\n\tif !rlm.reached {\n\t\tt.Error(\"The ratelimit must run the done function.\")\n\t}\n\n\t_, _ = Server(func(o *options) {\n\t\to.limiter = rlrm\n\t})(nextValid)(context.Background(), nil)\n\tif rlrm.reached {\n\t\tt.Error(\"The ratelimit must not run the done function and should be denied.\")\n\t}\n}\n"
  },
  {
    "path": "middleware/recovery/recovery.go",
    "content": "package recovery\n\nimport (\n\t\"context\"\n\t\"runtime\"\n\t\"time\"\n\n\t\"github.com/go-kratos/kratos/v2/errors\"\n\t\"github.com/go-kratos/kratos/v2/log\"\n\t\"github.com/go-kratos/kratos/v2/middleware\"\n)\n\n// Latency is recovery latency context key\ntype Latency struct{}\n\n// ErrUnknownRequest is unknown request error.\nvar ErrUnknownRequest = errors.InternalServer(\"UNKNOWN\", \"unknown request error\")\n\n// HandlerFunc is recovery handler func.\ntype HandlerFunc func(ctx context.Context, req, err any) error\n\n// Option is recovery option.\ntype Option func(*options)\n\ntype options struct {\n\thandler HandlerFunc\n}\n\n// WithHandler with recovery handler.\nfunc WithHandler(h HandlerFunc) Option {\n\treturn func(o *options) {\n\t\to.handler = h\n\t}\n}\n\n// Recovery is a server middleware that recovers from any panics.\nfunc Recovery(opts ...Option) middleware.Middleware {\n\top := options{\n\t\thandler: func(context.Context, any, any) error {\n\t\t\treturn ErrUnknownRequest\n\t\t},\n\t}\n\tfor _, o := range opts {\n\t\to(&op)\n\t}\n\treturn func(handler middleware.Handler) middleware.Handler {\n\t\treturn func(ctx context.Context, req any) (reply any, err error) {\n\t\t\tstartTime := time.Now()\n\t\t\tdefer func() {\n\t\t\t\tif rerr := recover(); rerr != nil {\n\t\t\t\t\tbuf := make([]byte, 64<<10) //nolint:mnd\n\t\t\t\t\tn := runtime.Stack(buf, false)\n\t\t\t\t\tbuf = buf[:n]\n\t\t\t\t\tlog.Context(ctx).Errorf(\"%v: %+v\\n%s\\n\", rerr, req, buf)\n\t\t\t\t\tctx = context.WithValue(ctx, Latency{}, time.Since(startTime).Seconds())\n\t\t\t\t\terr = op.handler(ctx, req, rerr)\n\t\t\t\t}\n\t\t\t}()\n\t\t\treturn handler(ctx, req)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "middleware/recovery/recovery_test.go",
    "content": "package recovery\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"testing\"\n\n\t\"github.com/go-kratos/kratos/v2/errors\"\n)\n\nfunc TestOnce(t *testing.T) {\n\tdefer func() {\n\t\tif recover() != nil {\n\t\t\tt.Error(\"fail\")\n\t\t}\n\t}()\n\n\tnext := func(context.Context, any) (any, error) {\n\t\tpanic(\"panic reason\")\n\t}\n\t_, e := Recovery(WithHandler(func(ctx context.Context, _, err any) error {\n\t\t_, ok := ctx.Value(Latency{}).(float64)\n\t\tif !ok {\n\t\t\tt.Errorf(\"not latency\")\n\t\t}\n\t\treturn errors.InternalServer(\"RECOVERY\", fmt.Sprintf(\"panic triggered: %v\", err))\n\t}))(next)(context.Background(), \"panic\")\n\tt.Logf(\"succ and reason is %v\", e)\n}\n\nfunc TestNotPanic(t *testing.T) {\n\tnext := func(_ context.Context, req any) (any, error) {\n\t\treturn req.(string) + \"https://go-kratos.dev\", nil\n\t}\n\n\t_, e := Recovery(WithHandler(func(_ context.Context, _ any, err any) error {\n\t\treturn errors.InternalServer(\"RECOVERY\", fmt.Sprintf(\"panic triggered: %v\", err))\n\t}))(next)(context.Background(), \"notPanic\")\n\tif e != nil {\n\t\tt.Errorf(\"e isn't nil\")\n\t}\n}\n"
  },
  {
    "path": "middleware/selector/selector.go",
    "content": "package selector\n\nimport (\n\t\"context\"\n\t\"regexp\"\n\t\"strings\"\n\n\t\"github.com/go-kratos/kratos/v2/middleware\"\n\t\"github.com/go-kratos/kratos/v2/transport\"\n)\n\ntype (\n\ttransporter func(ctx context.Context) (transport.Transporter, bool)\n\tMatchFunc   func(ctx context.Context, operation string) bool\n)\n\nvar (\n\t// serverTransporter is get server transport.Transporter from ctx\n\tserverTransporter transporter = func(ctx context.Context) (transport.Transporter, bool) {\n\t\treturn transport.FromServerContext(ctx)\n\t}\n\t// clientTransporter is get client transport.Transporter from ctx\n\tclientTransporter transporter = func(ctx context.Context) (transport.Transporter, bool) {\n\t\treturn transport.FromClientContext(ctx)\n\t}\n)\n\n// Builder is a selector builder\ntype Builder struct {\n\tclient bool\n\n\tprefix   []string\n\tregex    []string\n\tpath     []string\n\tmatch    MatchFunc\n\tcompiled []*regexp.Regexp\n\n\tms []middleware.Middleware\n}\n\n// Server selector middleware\nfunc Server(ms ...middleware.Middleware) *Builder {\n\treturn &Builder{ms: ms}\n}\n\n// Client selector middleware\nfunc Client(ms ...middleware.Middleware) *Builder {\n\treturn &Builder{client: true, ms: ms}\n}\n\n// Prefix is with Builder's prefix\nfunc (b *Builder) Prefix(prefix ...string) *Builder {\n\tb.prefix = prefix\n\treturn b\n}\n\n// Regex is with Builder's regex\nfunc (b *Builder) Regex(regex ...string) *Builder {\n\tb.regex = regex\n\treturn b\n}\n\n// Path is with Builder's path\nfunc (b *Builder) Path(path ...string) *Builder {\n\tb.path = path\n\treturn b\n}\n\n// Match is with Builder's match\nfunc (b *Builder) Match(fn MatchFunc) *Builder {\n\tb.match = fn\n\treturn b\n}\n\n// Build is Builder's Build, for example: Server().Path(m1,m2).Build()\nfunc (b *Builder) Build() middleware.Middleware {\n\tvar transporter func(ctx context.Context) (transport.Transporter, bool)\n\tif b.client {\n\t\ttransporter = clientTransporter\n\t} else {\n\t\ttransporter = serverTransporter\n\t}\n\tb.compiled = make([]*regexp.Regexp, 0, len(b.regex))\n\tfor _, regex := range b.regex {\n\t\tif r, err := regexp.Compile(regex); err == nil {\n\t\t\tb.compiled = append(b.compiled, r)\n\t\t}\n\t}\n\treturn selector(transporter, b.matches, b.ms...)\n}\n\n// matches is match operation compliance Builder\nfunc (b *Builder) matches(ctx context.Context, transporter transporter) bool {\n\tinfo, ok := transporter(ctx)\n\tif !ok {\n\t\treturn false\n\t}\n\n\toperation := info.Operation()\n\tfor _, prefix := range b.prefix {\n\t\tif prefixMatch(prefix, operation) {\n\t\t\treturn true\n\t\t}\n\t}\n\tfor _, r := range b.compiled {\n\t\tif r.FindString(operation) == operation {\n\t\t\treturn true\n\t\t}\n\t}\n\tfor _, path := range b.path {\n\t\tif pathMatch(path, operation) {\n\t\t\treturn true\n\t\t}\n\t}\n\n\tif b.match != nil {\n\t\tif b.match(ctx, operation) {\n\t\t\treturn true\n\t\t}\n\t}\n\n\treturn false\n}\n\n// selector middleware\nfunc selector(transporter transporter, match func(context.Context, transporter) bool, ms ...middleware.Middleware) middleware.Middleware {\n\treturn func(handler middleware.Handler) middleware.Handler {\n\t\treturn func(ctx context.Context, req any) (reply any, err error) {\n\t\t\tif !match(ctx, transporter) {\n\t\t\t\treturn handler(ctx, req)\n\t\t\t}\n\t\t\treturn middleware.Chain(ms...)(handler)(ctx, req)\n\t\t}\n\t}\n}\n\nfunc pathMatch(path string, operation string) bool {\n\treturn path == operation\n}\n\nfunc prefixMatch(prefix string, operation string) bool {\n\treturn strings.HasPrefix(operation, prefix)\n}\n"
  },
  {
    "path": "middleware/selector/selector_test.go",
    "content": "package selector\n\nimport (\n\t\"context\"\n\t\"reflect\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/go-kratos/kratos/v2/middleware\"\n\t\"github.com/go-kratos/kratos/v2/transport\"\n)\n\nvar _ transport.Transporter = (*Transport)(nil)\n\ntype Transport struct {\n\tkind      transport.Kind\n\tendpoint  string\n\toperation string\n\theaders   *mockHeader\n}\n\nfunc (tr *Transport) Kind() transport.Kind {\n\treturn tr.kind\n}\n\nfunc (tr *Transport) Endpoint() string {\n\treturn tr.endpoint\n}\n\nfunc (tr *Transport) Operation() string {\n\treturn tr.operation\n}\n\nfunc (tr *Transport) RequestHeader() transport.Header {\n\treturn tr.headers\n}\n\nfunc (tr *Transport) ReplyHeader() transport.Header {\n\treturn nil\n}\n\ntype mockHeader struct {\n\tm map[string][]string\n}\n\nfunc (m *mockHeader) Get(key string) string {\n\tvals := m.m[key]\n\tif len(vals) > 0 {\n\t\treturn vals[0]\n\t}\n\treturn \"\"\n}\n\nfunc (m *mockHeader) Set(key, value string) {\n\tm.m[key] = []string{value}\n}\n\nfunc (m *mockHeader) Add(key, value string) {\n\tm.m[key] = append(m.m[key], value)\n}\n\nfunc (m *mockHeader) Keys() []string {\n\tkeys := make([]string, 0, len(m.m))\n\tfor k := range m.m {\n\t\tkeys = append(keys, k)\n\t}\n\treturn keys\n}\n\nfunc (m *mockHeader) Values(key string) []string {\n\treturn m.m[key]\n}\n\nfunc TestMatch(t *testing.T) {\n\ttests := []struct {\n\t\tname string\n\t\tctx  context.Context\n\t}{\n\t\t// TODO: Add test cases.\n\t\t{\n\t\t\tname: \"/hello/world\",\n\t\t\tctx:  transport.NewServerContext(context.Background(), &Transport{operation: \"/hello/world\"}),\n\t\t},\n\t\t{\n\t\t\tname: \"/hi/world\",\n\t\t\tctx:  transport.NewServerContext(context.Background(), &Transport{operation: \"/hi/world\"}),\n\t\t},\n\t\t{\n\t\t\tname: \"/test/1234\",\n\t\t\tctx:  transport.NewServerContext(context.Background(), &Transport{operation: \"/test/1234\"}),\n\t\t},\n\t\t{\n\t\t\tname: \"/example/kratos\",\n\t\t\tctx:  transport.NewServerContext(context.Background(), &Transport{operation: \"/example/kratos\"}),\n\t\t},\n\t}\n\tfor _, test := range tests {\n\t\tt.Run(test.name, func(t *testing.T) {\n\t\t\tnext := func(_ context.Context, req any) (any, error) {\n\t\t\t\tt.Log(req)\n\t\t\t\treturn \"reply\", nil\n\t\t\t}\n\t\t\tnext = Server(testMiddleware).Prefix(\"/hello/\").Regex(`/test/[0-9]+`).\n\t\t\t\tPath(\"/example/kratos\").Build()(next)\n\t\t\t_, _ = next(test.ctx, test.name)\n\t\t})\n\t}\n}\n\nfunc TestMatchClient(t *testing.T) {\n\ttests := []struct {\n\t\tname string\n\t\tctx  context.Context\n\t}{\n\t\t// TODO: Add test cases.\n\t\t{\n\t\t\tname: \"/hello/world\",\n\t\t\tctx:  transport.NewClientContext(context.Background(), &Transport{operation: \"/hello/world\"}),\n\t\t},\n\t\t{\n\t\t\tname: \"/hi/world\",\n\t\t\tctx:  transport.NewClientContext(context.Background(), &Transport{operation: \"/hi/world\"}),\n\t\t},\n\t\t{\n\t\t\tname: \"/test/1234\",\n\t\t\tctx:  transport.NewClientContext(context.Background(), &Transport{operation: \"/test/1234\"}),\n\t\t},\n\t\t{\n\t\t\tname: \"/example/kratos\",\n\t\t\tctx:  transport.NewClientContext(context.Background(), &Transport{operation: \"/example/kratos\"}),\n\t\t},\n\t}\n\tfor _, test := range tests {\n\t\tt.Run(test.name, func(t *testing.T) {\n\t\t\tnext := func(_ context.Context, req any) (any, error) {\n\t\t\t\tt.Log(req)\n\t\t\t\treturn \"reply\", nil\n\t\t\t}\n\t\t\tnext = Client(testMiddleware).Prefix(\"/hello/\").Regex(`/test/[0-9]+`).\n\t\t\t\tPath(\"/example/kratos\").Build()(next)\n\t\t\t_, _ = next(test.ctx, test.name)\n\t\t})\n\t}\n}\n\nfunc TestFunc(t *testing.T) {\n\ttests := []struct {\n\t\tname string\n\t\tctx  context.Context\n\t}{\n\t\t{\n\t\t\tname: \"/hello.Update/world\",\n\t\t\tctx:  transport.NewServerContext(context.Background(), &Transport{operation: \"/hello.Update/world\"}),\n\t\t},\n\t\t{\n\t\t\tname: \"/hi.Create/world\",\n\t\t\tctx:  transport.NewServerContext(context.Background(), &Transport{operation: \"/hi.Create/world\"}),\n\t\t},\n\t\t{\n\t\t\tname: \"/test.Name/1234\",\n\t\t\tctx:  transport.NewServerContext(context.Background(), &Transport{operation: \"/test.Name/1234\"}),\n\t\t},\n\t\t{\n\t\t\tname: \"/go-kratos.dev/kratos\",\n\t\t\tctx:  transport.NewServerContext(context.Background(), &Transport{operation: \"/go-kratos.dev/kratos\"}),\n\t\t},\n\t}\n\tfor _, test := range tests {\n\t\tt.Run(test.name, func(t *testing.T) {\n\t\t\tnext := func(_ context.Context, req any) (any, error) {\n\t\t\t\tt.Log(req)\n\t\t\t\treturn \"reply\", nil\n\t\t\t}\n\t\t\tnext = Server(testMiddleware).Match(func(_ context.Context, operation string) bool {\n\t\t\t\tif strings.HasPrefix(operation, \"/go-kratos.dev\") || strings.HasSuffix(operation, \"world\") {\n\t\t\t\t\treturn true\n\t\t\t\t}\n\t\t\t\treturn false\n\t\t\t}).Build()(next)\n\t\t\treply, err := next(test.ctx, test.name)\n\t\t\tif err != nil {\n\t\t\t\tt.Errorf(\"expect error is nil, but got %v\", err)\n\t\t\t}\n\t\t\tif !reflect.DeepEqual(reply, \"reply\") {\n\t\t\t\tt.Errorf(\"expect reply is reply,but got %v\", reply)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestHeaderFunc(t *testing.T) {\n\ttests := []struct {\n\t\tname string\n\t\tctx  context.Context\n\t}{\n\t\t{\n\t\t\tname: \"/hello.Update/world\",\n\t\t\tctx: transport.NewServerContext(context.Background(), &Transport{\n\t\t\t\toperation: \"/hello.Update/world\",\n\t\t\t\theaders:   &mockHeader{map[string][]string{\"X-Test\": {\"test\"}}},\n\t\t\t}),\n\t\t},\n\t\t{\n\t\t\tname: \"/hi.Create/world\",\n\t\t\tctx: transport.NewServerContext(context.Background(), &Transport{\n\t\t\t\toperation: \"/hi.Create/world\",\n\t\t\t\theaders:   &mockHeader{map[string][]string{\"X-Test\": {\"test2\"}, \"go-kratos\": {\"kratos\"}}},\n\t\t\t}),\n\t\t},\n\t\t{\n\t\t\tname: \"/test.Name/1234\",\n\t\t\tctx: transport.NewServerContext(context.Background(), &Transport{\n\t\t\t\toperation: \"/test.Name/1234\",\n\t\t\t\theaders:   &mockHeader{map[string][]string{\"X-Test\": {\"test3\"}}},\n\t\t\t}),\n\t\t},\n\t\t{\n\t\t\tname: \"/go-kratos.dev/kratos\",\n\t\t\tctx: transport.NewServerContext(context.Background(), &Transport{\n\t\t\t\toperation: \"/go-kratos.dev/kratos\",\n\t\t\t\theaders:   &mockHeader{map[string][]string{\"X-Test\": {\"test\"}}},\n\t\t\t}),\n\t\t},\n\t}\n\tfor _, test := range tests {\n\t\tt.Run(test.name, func(t *testing.T) {\n\t\t\tnext := func(_ context.Context, req any) (any, error) {\n\t\t\t\tt.Log(req)\n\t\t\t\treturn \"reply\", nil\n\t\t\t}\n\t\t\tnext = Server(testMiddleware).Match(func(ctx context.Context, _ string) bool {\n\t\t\t\ttr, ok := transport.FromServerContext(ctx)\n\t\t\t\tif !ok {\n\t\t\t\t\treturn false\n\t\t\t\t}\n\t\t\t\tif tr.RequestHeader().Get(\"X-Test\") == \"test\" {\n\t\t\t\t\treturn true\n\t\t\t\t}\n\t\t\t\tif tr.RequestHeader().Get(\"go-kratos\") == \"kratos\" {\n\t\t\t\t\treturn true\n\t\t\t\t}\n\t\t\t\treturn false\n\t\t\t}).Build()(next)\n\t\t\treply, err := next(test.ctx, test.name)\n\t\t\tif err != nil {\n\t\t\t\tt.Errorf(\"expect error is nil, but got %v\", err)\n\t\t\t}\n\t\t\tif !reflect.DeepEqual(reply, \"reply\") {\n\t\t\t\tt.Errorf(\"expect reply is reply,but got %v\", reply)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc testMiddleware(handler middleware.Handler) middleware.Handler {\n\treturn func(ctx context.Context, req any) (reply any, err error) {\n\t\treply, err = handler(ctx, req)\n\t\treturn\n\t}\n}\n\nfunc Test_RegexMatch(t *testing.T) {\n\ttests := []struct {\n\t\tname      string\n\t\tregex     []string\n\t\toperation string\n\t\twant      bool\n\t}{\n\t\t{\n\t\t\tname:      \"exact match with digits\",\n\t\t\tregex:     []string{`/test/[0-9]+`},\n\t\t\toperation: \"/test/1234\",\n\t\t\twant:      true,\n\t\t},\n\t\t{\n\t\t\tname:      \"no match\",\n\t\t\tregex:     []string{`/test/[0-9]+`},\n\t\t\toperation: \"/test/abc\",\n\t\t\twant:      false,\n\t\t},\n\t\t{\n\t\t\tname:      \"multiple patterns first matches\",\n\t\t\tregex:     []string{`/api/v[0-9]+/.*`, `/test/.*`},\n\t\t\toperation: \"/api/v2/users\",\n\t\t\twant:      true,\n\t\t},\n\t\t{\n\t\t\tname:      \"multiple patterns second matches\",\n\t\t\tregex:     []string{`/api/v[0-9]+/.*`, `/test/.*`},\n\t\t\toperation: \"/test/hello\",\n\t\t\twant:      true,\n\t\t},\n\t\t{\n\t\t\tname:      \"multiple patterns none match\",\n\t\t\tregex:     []string{`/api/v[0-9]+/.*`, `/test/[0-9]+`},\n\t\t\toperation: \"/other/path\",\n\t\t\twant:      false,\n\t\t},\n\t\t{\n\t\t\tname:      \"invalid regex is skipped\",\n\t\t\tregex:     []string{\"^\\b(?\"},\n\t\t\toperation: \"something\",\n\t\t\twant:      false,\n\t\t},\n\t\t{\n\t\t\tname:      \"invalid regex mixed with valid\",\n\t\t\tregex:     []string{\"^\\b(?\", `/test/[0-9]+`},\n\t\t\toperation: \"/test/1234\",\n\t\t\twant:      true,\n\t\t},\n\t\t{\n\t\t\tname:      \"empty regex list\",\n\t\t\tregex:     []string{},\n\t\t\toperation: \"/test/1234\",\n\t\t\twant:      false,\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tvar middlewareApplied bool\n\t\t\tmarkMiddleware := func(handler middleware.Handler) middleware.Handler {\n\t\t\t\treturn func(ctx context.Context, req any) (any, error) {\n\t\t\t\t\tmiddlewareApplied = true\n\t\t\t\t\treturn handler(ctx, req)\n\t\t\t\t}\n\t\t\t}\n\t\t\tnext := func(_ context.Context, _ any) (any, error) {\n\t\t\t\treturn \"reply\", nil\n\t\t\t}\n\t\t\tctx := transport.NewServerContext(context.Background(), &Transport{operation: tt.operation})\n\t\t\thandler := Server(markMiddleware).Regex(tt.regex...).Build()(next)\n\t\t\t_, _ = handler(ctx, tt.operation)\n\t\t\tif middlewareApplied != tt.want {\n\t\t\t\tt.Errorf(\"middleware applied = %v, want %v\", middlewareApplied, tt.want)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc Test_InvalidRegexSkipped(t *testing.T) {\n\tb := Server(testMiddleware).Regex(\"^\\b(?\", `/valid/[0-9]+`)\n\tm := b.Build()\n\tif m == nil {\n\t\tt.Fatal(\"Build() must not return nil\")\n\t}\n\tif len(b.compiled) != 1 {\n\t\tt.Errorf(\"expected 1 compiled regex, got %d\", len(b.compiled))\n\t}\n}\n\nfunc Test_matches(t *testing.T) {\n\tb := Builder{}\n\tif b.matches(context.Background(), func(_ context.Context) (transport.Transporter, bool) { return nil, false }) {\n\t\tt.Error(\"The matches method must return false.\")\n\t}\n}\n"
  },
  {
    "path": "middleware/tracing/metadata.go",
    "content": "package tracing\n\nimport (\n\t\"context\"\n\n\t\"go.opentelemetry.io/otel/propagation\"\n\n\t\"github.com/go-kratos/kratos/v2\"\n\t\"github.com/go-kratos/kratos/v2/metadata\"\n)\n\nconst serviceHeader = \"x-md-service-name\"\n\n// Metadata is tracing metadata propagator\ntype Metadata struct{}\n\nvar _ propagation.TextMapPropagator = Metadata{}\n\n// Inject sets metadata key-values from ctx into the carrier.\nfunc (b Metadata) Inject(ctx context.Context, carrier propagation.TextMapCarrier) {\n\tapp, ok := kratos.FromContext(ctx)\n\tif ok {\n\t\tcarrier.Set(serviceHeader, app.Name())\n\t}\n}\n\n// Extract returns a copy of parent with the metadata from the carrier added.\nfunc (b Metadata) Extract(parent context.Context, carrier propagation.TextMapCarrier) context.Context {\n\tname := carrier.Get(serviceHeader)\n\tif name == \"\" {\n\t\treturn parent\n\t}\n\tif md, ok := metadata.FromServerContext(parent); ok {\n\t\tmd.Set(serviceHeader, name)\n\t\treturn parent\n\t}\n\tmd := metadata.New()\n\tmd.Set(serviceHeader, name)\n\tparent = metadata.NewServerContext(parent, md)\n\treturn parent\n}\n\n// Fields returns the keys whose values are set with Inject.\nfunc (b Metadata) Fields() []string {\n\treturn []string{serviceHeader}\n}\n"
  },
  {
    "path": "middleware/tracing/metadata_test.go",
    "content": "package tracing\n\nimport (\n\t\"context\"\n\t\"reflect\"\n\t\"testing\"\n\n\t\"github.com/go-kratos/kratos/v2\"\n\t\"github.com/go-kratos/kratos/v2/metadata\"\n\n\t\"go.opentelemetry.io/otel/propagation\"\n)\n\nfunc TestMetadata_Inject(t *testing.T) {\n\ttype args struct {\n\t\tappName string\n\t\tcarrier propagation.TextMapCarrier\n\t}\n\ttests := []struct {\n\t\tname string\n\t\targs args\n\t\twant string\n\t}{\n\t\t{\n\t\t\tname: \"https://go-kratos.dev\",\n\t\t\targs: args{\"https://go-kratos.dev\", propagation.HeaderCarrier{}},\n\t\t\twant: \"https://go-kratos.dev\",\n\t\t},\n\t\t{\n\t\t\tname: \"https://github.com/go-kratos/kratos\",\n\t\t\targs: args{\"https://github.com/go-kratos/kratos\", propagation.HeaderCarrier{\"mode\": []string{\"test\"}}},\n\t\t\twant: \"https://github.com/go-kratos/kratos\",\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\ta := kratos.New(kratos.Name(tt.args.appName))\n\t\t\tctx := kratos.NewContext(context.Background(), a)\n\t\t\tm := new(Metadata)\n\t\t\tm.Inject(ctx, tt.args.carrier)\n\t\t\tif res := tt.args.carrier.Get(serviceHeader); tt.want != res {\n\t\t\t\tt.Errorf(\"Get(serviceHeader) :%s want: %s\", res, tt.want)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestMetadata_Extract(t *testing.T) {\n\ttype args struct {\n\t\tparent  context.Context\n\t\tcarrier propagation.TextMapCarrier\n\t}\n\ttests := []struct {\n\t\tname  string\n\t\targs  args\n\t\twant  string\n\t\tcrash bool\n\t}{\n\t\t{\n\t\t\tname: \"https://go-kratos.dev\",\n\t\t\targs: args{\n\t\t\t\tparent:  context.Background(),\n\t\t\t\tcarrier: propagation.HeaderCarrier{\"X-Md-Service-Name\": []string{\"https://go-kratos.dev\"}},\n\t\t\t},\n\t\t\twant: \"https://go-kratos.dev\",\n\t\t},\n\t\t{\n\t\t\tname: \"https://github.com/go-kratos/kratos\",\n\t\t\targs: args{\n\t\t\t\tparent:  metadata.NewServerContext(context.Background(), metadata.Metadata{}),\n\t\t\t\tcarrier: propagation.HeaderCarrier{\"X-Md-Service-Name\": []string{\"https://github.com/go-kratos/kratos\"}},\n\t\t\t},\n\t\t\twant: \"https://github.com/go-kratos/kratos\",\n\t\t},\n\t\t{\n\t\t\tname: \"https://github.com/go-kratos/kratos\",\n\t\t\targs: args{\n\t\t\t\tparent:  metadata.NewServerContext(context.Background(), metadata.Metadata{}),\n\t\t\t\tcarrier: propagation.HeaderCarrier{\"X-Md-Service-Name\": nil},\n\t\t\t},\n\t\t\tcrash: true,\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tb := Metadata{}\n\t\t\tctx := b.Extract(tt.args.parent, tt.args.carrier)\n\t\t\tmd, ok := metadata.FromServerContext(ctx)\n\t\t\tif !ok {\n\t\t\t\tif tt.crash {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tt.Errorf(\"expect %v, got %v\", true, ok)\n\t\t\t}\n\t\t\tif !reflect.DeepEqual(md.Get(serviceHeader), tt.want) {\n\t\t\t\tt.Errorf(\"expect %v, got %v\", tt.want, md.Get(serviceHeader))\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestFields(t *testing.T) {\n\tb := Metadata{}\n\tif !reflect.DeepEqual(b.Fields(), []string{\"x-md-service-name\"}) {\n\t\tt.Errorf(\"expect %v, got %v\", []string{\"x-md-service-name\"}, b.Fields())\n\t}\n}\n"
  },
  {
    "path": "middleware/tracing/span.go",
    "content": "package tracing\n\nimport (\n\t\"context\"\n\t\"net\"\n\t\"net/url\"\n\t\"strings\"\n\n\t\"github.com/go-kratos/kratos/v2/metadata\"\n\t\"github.com/go-kratos/kratos/v2/transport\"\n\t\"github.com/go-kratos/kratos/v2/transport/http\"\n\n\t\"go.opentelemetry.io/otel/attribute\"\n\tsemconv \"go.opentelemetry.io/otel/semconv/v1.4.0\"\n\t\"go.opentelemetry.io/otel/trace\"\n\t\"google.golang.org/grpc/peer\"\n\t\"google.golang.org/protobuf/proto\"\n)\n\nfunc setClientSpan(ctx context.Context, span trace.Span, m any) {\n\tvar (\n\t\tattrs     []attribute.KeyValue\n\t\tremote    string\n\t\toperation string\n\t\trpcKind   string\n\t)\n\ttr, ok := transport.FromClientContext(ctx)\n\tif ok {\n\t\toperation = tr.Operation()\n\t\trpcKind = tr.Kind().String()\n\t\tswitch tr.Kind() {\n\t\tcase transport.KindHTTP:\n\t\t\tif ht, ok := tr.(http.Transporter); ok {\n\t\t\t\tmethod := ht.Request().Method\n\t\t\t\troute := ht.PathTemplate()\n\t\t\t\tpath := ht.Request().URL.Path\n\t\t\t\tattrs = append(attrs, semconv.HTTPMethodKey.String(method))\n\t\t\t\tattrs = append(attrs, semconv.HTTPRouteKey.String(route))\n\t\t\t\tattrs = append(attrs, semconv.HTTPTargetKey.String(path))\n\t\t\t\tremote = ht.Request().Host\n\t\t\t}\n\t\tcase transport.KindGRPC:\n\t\t\tremote, _ = parseTarget(tr.Endpoint())\n\t\t}\n\t}\n\tattrs = append(attrs, semconv.RPCSystemKey.String(rpcKind))\n\t_, mAttrs := parseFullMethod(operation)\n\tattrs = append(attrs, mAttrs...)\n\tif remote != \"\" {\n\t\tattrs = append(attrs, peerAttr(remote)...)\n\t}\n\tif p, ok := m.(proto.Message); ok {\n\t\tattrs = append(attrs, attribute.Key(\"send_msg.size\").Int(proto.Size(p)))\n\t}\n\n\tspan.SetAttributes(attrs...)\n}\n\nfunc setServerSpan(ctx context.Context, span trace.Span, m any) {\n\tvar (\n\t\tattrs     []attribute.KeyValue\n\t\tremote    string\n\t\toperation string\n\t\trpcKind   string\n\t)\n\ttr, ok := transport.FromServerContext(ctx)\n\tif ok {\n\t\toperation = tr.Operation()\n\t\trpcKind = tr.Kind().String()\n\t\tswitch tr.Kind() {\n\t\tcase transport.KindHTTP:\n\t\t\tif ht, ok := tr.(http.Transporter); ok {\n\t\t\t\tmethod := ht.Request().Method\n\t\t\t\troute := ht.PathTemplate()\n\t\t\t\tpath := ht.Request().URL.Path\n\t\t\t\tattrs = append(attrs, semconv.HTTPMethodKey.String(method))\n\t\t\t\tattrs = append(attrs, semconv.HTTPRouteKey.String(route))\n\t\t\t\tattrs = append(attrs, semconv.HTTPTargetKey.String(path))\n\t\t\t\tremote = ht.Request().RemoteAddr\n\t\t\t}\n\t\tcase transport.KindGRPC:\n\t\t\tif p, ok := peer.FromContext(ctx); ok {\n\t\t\t\tremote = p.Addr.String()\n\t\t\t}\n\t\t}\n\t}\n\tattrs = append(attrs, semconv.RPCSystemKey.String(rpcKind))\n\t_, mAttrs := parseFullMethod(operation)\n\tattrs = append(attrs, mAttrs...)\n\tattrs = append(attrs, peerAttr(remote)...)\n\tif p, ok := m.(proto.Message); ok {\n\t\tattrs = append(attrs, attribute.Key(\"recv_msg.size\").Int(proto.Size(p)))\n\t}\n\tif md, ok := metadata.FromServerContext(ctx); ok {\n\t\tattrs = append(attrs, semconv.PeerServiceKey.String(md.Get(serviceHeader)))\n\t}\n\n\tspan.SetAttributes(attrs...)\n}\n\n// parseFullMethod returns a span name following the OpenTelemetry semantic\n// conventions as well as all applicable span attribute.KeyValue attributes based\n// on a gRPC's FullMethod.\nfunc parseFullMethod(fullMethod string) (string, []attribute.KeyValue) {\n\tname := strings.TrimLeft(fullMethod, \"/\")\n\tparts := strings.SplitN(name, \"/\", 2)\n\tif len(parts) != 2 { //nolint:mnd\n\t\t// Invalid format, does not follow `/package.service/method`.\n\t\treturn name, []attribute.KeyValue{attribute.Key(\"rpc.operation\").String(fullMethod)}\n\t}\n\n\tvar attrs []attribute.KeyValue\n\tif service := parts[0]; service != \"\" {\n\t\tattrs = append(attrs, semconv.RPCServiceKey.String(service))\n\t}\n\tif method := parts[1]; method != \"\" {\n\t\tattrs = append(attrs, semconv.RPCMethodKey.String(method))\n\t}\n\treturn name, attrs\n}\n\n// peerAttr returns attributes about the peer address.\nfunc peerAttr(addr string) []attribute.KeyValue {\n\thost, port, err := net.SplitHostPort(addr)\n\tif err != nil {\n\t\treturn []attribute.KeyValue(nil)\n\t}\n\n\tif host == \"\" {\n\t\thost = \"127.0.0.1\"\n\t}\n\n\treturn []attribute.KeyValue{\n\t\tsemconv.NetPeerIPKey.String(host),\n\t\tsemconv.NetPeerPortKey.String(port),\n\t}\n}\n\nfunc parseTarget(endpoint string) (address string, err error) {\n\tvar u *url.URL\n\tu, err = url.Parse(endpoint)\n\tif err != nil {\n\t\tif u, err = url.Parse(\"http://\" + endpoint); err != nil {\n\t\t\treturn \"\", err\n\t\t}\n\t\treturn u.Host, nil\n\t}\n\tif len(u.Path) > 1 {\n\t\treturn u.Path[1:], nil\n\t}\n\treturn endpoint, nil\n}\n"
  },
  {
    "path": "middleware/tracing/span_test.go",
    "content": "package tracing\n\nimport (\n\t\"context\"\n\t\"net\"\n\t\"net/http\"\n\t\"reflect\"\n\t\"testing\"\n\n\t\"go.opentelemetry.io/otel/attribute\"\n\tsemconv \"go.opentelemetry.io/otel/semconv/v1.4.0\"\n\t\"google.golang.org/grpc/peer\"\n\n\t\"go.opentelemetry.io/otel/trace/noop\"\n\n\t\"github.com/go-kratos/kratos/v2/internal/testdata/binding\"\n\t\"github.com/go-kratos/kratos/v2/metadata\"\n\t\"github.com/go-kratos/kratos/v2/transport\"\n)\n\nfunc Test_parseFullMethod(t *testing.T) {\n\ttests := []struct {\n\t\tname       string\n\t\tfullMethod string\n\t\twant       string\n\t\twantAttr   []attribute.KeyValue\n\t}{\n\t\t{\n\t\t\tname:       \"/foo.bar/hello\",\n\t\t\tfullMethod: \"/foo.bar/hello\",\n\t\t\twant:       \"foo.bar/hello\",\n\t\t\twantAttr: []attribute.KeyValue{\n\t\t\t\tsemconv.RPCServiceKey.String(\"foo.bar\"),\n\t\t\t\tsemconv.RPCMethodKey.String(\"hello\"),\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:       \"/foo.bar/hello/world\",\n\t\t\tfullMethod: \"/foo.bar/hello/world\",\n\t\t\twant:       \"foo.bar/hello/world\",\n\t\t\twantAttr: []attribute.KeyValue{\n\t\t\t\tsemconv.RPCServiceKey.String(\"foo.bar\"),\n\t\t\t\tsemconv.RPCMethodKey.String(\"hello/world\"),\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:       \"/hello\",\n\t\t\tfullMethod: \"/hello\",\n\t\t\twant:       \"hello\",\n\t\t\twantAttr:   []attribute.KeyValue{attribute.Key(\"rpc.operation\").String(\"/hello\")},\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tgot, got1 := parseFullMethod(tt.fullMethod)\n\t\t\tif got != tt.want {\n\t\t\t\tt.Errorf(\"parseFullMethod() got = %v, want %v\", got, tt.want)\n\t\t\t}\n\t\t\tif !reflect.DeepEqual(got1, tt.wantAttr) {\n\t\t\t\tt.Errorf(\"parseFullMethod() got1 = %v, want %v\", got1, tt.wantAttr)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc Test_peerAttr(t *testing.T) {\n\ttests := []struct {\n\t\tname string\n\t\taddr string\n\t\twant []attribute.KeyValue\n\t}{\n\t\t{\n\t\t\tname: \"nil addr\",\n\t\t\taddr: \":8080\",\n\t\t\twant: []attribute.KeyValue{\n\t\t\t\tsemconv.NetPeerIPKey.String(\"127.0.0.1\"),\n\t\t\t\tsemconv.NetPeerPortKey.String(\"8080\"),\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"normal addr without port\",\n\t\t\taddr: \"192.168.0.1\",\n\t\t\twant: []attribute.KeyValue(nil),\n\t\t},\n\t\t{\n\t\t\tname: \"normal addr with port\",\n\t\t\taddr: \"192.168.0.1:8080\",\n\t\t\twant: []attribute.KeyValue{\n\t\t\t\tsemconv.NetPeerIPKey.String(\"192.168.0.1\"),\n\t\t\t\tsemconv.NetPeerPortKey.String(\"8080\"),\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"dns addr\",\n\t\t\taddr: \"foo:8080\",\n\t\t\twant: []attribute.KeyValue{\n\t\t\t\tsemconv.NetPeerIPKey.String(\"foo\"),\n\t\t\t\tsemconv.NetPeerPortKey.String(\"8080\"),\n\t\t\t},\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tif got := peerAttr(tt.addr); !reflect.DeepEqual(got, tt.want) {\n\t\t\t\tt.Errorf(\"peerAttr() = %v, want %v\", got, tt.want)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc Test_parseTarget(t *testing.T) {\n\ttests := []struct {\n\t\tname        string\n\t\tendpoint    string\n\t\twantAddress string\n\t\twantErr     bool\n\t}{\n\t\t{\n\t\t\tname:        \"http\",\n\t\t\tendpoint:    \"http://foo.bar:8080\",\n\t\t\twantAddress: \"http://foo.bar:8080\",\n\t\t\twantErr:     false,\n\t\t},\n\t\t{\n\t\t\tname:        \"http\",\n\t\t\tendpoint:    \"http://127.0.0.1:8080\",\n\t\t\twantAddress: \"http://127.0.0.1:8080\",\n\t\t\twantErr:     false,\n\t\t},\n\t\t{\n\t\t\tname:        \"without protocol\",\n\t\t\tendpoint:    \"foo.bar:8080\",\n\t\t\twantAddress: \"foo.bar:8080\",\n\t\t\twantErr:     false,\n\t\t},\n\t\t{\n\t\t\tname:        \"grpc\",\n\t\t\tendpoint:    \"grpc://foo.bar\",\n\t\t\twantAddress: \"grpc://foo.bar\",\n\t\t\twantErr:     false,\n\t\t},\n\t\t{\n\t\t\tname:        \"with path\",\n\t\t\tendpoint:    \"/foo\",\n\t\t\twantAddress: \"foo\",\n\t\t\twantErr:     false,\n\t\t},\n\t\t{\n\t\t\tname:        \"with path\",\n\t\t\tendpoint:    \"http://127.0.0.1/hello\",\n\t\t\twantAddress: \"hello\",\n\t\t\twantErr:     false,\n\t\t},\n\t\t{\n\t\t\tname:        \"empty\",\n\t\t\tendpoint:    \"%%\",\n\t\t\twantAddress: \"\",\n\t\t\twantErr:     true,\n\t\t},\n\t\t{\n\t\t\tname:        \"invalid path\",\n\t\t\tendpoint:    \"//%2F/#%2Fanother\",\n\t\t\twantAddress: \"\",\n\t\t\twantErr:     false,\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tgotAddress, err := parseTarget(tt.endpoint)\n\t\t\tif (err != nil) != tt.wantErr {\n\t\t\t\tt.Errorf(\"parseTarget() error = %v, wantErr %v\", err, tt.wantErr)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif gotAddress != tt.wantAddress {\n\t\t\t\tt.Errorf(\"parseTarget() = %v, want %v\", gotAddress, tt.wantAddress)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestSetServerSpan(_ *testing.T) {\n\tctx := context.Background()\n\t_, span := noop.NewTracerProvider().Tracer(\"Tracer\").Start(ctx, \"Spanname\")\n\n\t// Handle without Transport context\n\tsetServerSpan(ctx, span, nil)\n\n\t// Handle with proto message\n\tm := &binding.HelloRequest{}\n\tsetServerSpan(ctx, span, m)\n\n\t// Handle with metadata context\n\tctx = metadata.NewServerContext(ctx, metadata.New())\n\tsetServerSpan(ctx, span, m)\n\n\t// Handle with KindHTTP transport context\n\tmt := &mockTransport{\n\t\tkind: transport.KindHTTP,\n\t}\n\tmt.request, _ = http.NewRequest(http.MethodGet, \"/endpoint\", nil)\n\tctx = transport.NewServerContext(ctx, mt)\n\tsetServerSpan(ctx, span, m)\n\n\t// Handle with KindGRPC transport context\n\tmt.kind = transport.KindGRPC\n\tctx = transport.NewServerContext(ctx, mt)\n\tip, _ := net.ResolveIPAddr(\"ip\", \"1.1.1.1\")\n\tctx = peer.NewContext(ctx, &peer.Peer{\n\t\tAddr: ip,\n\t})\n\tsetServerSpan(ctx, span, m)\n}\n\nfunc TestSetClientSpan(_ *testing.T) {\n\tctx := context.Background()\n\t_, span := noop.NewTracerProvider().Tracer(\"Tracer\").Start(ctx, \"Spanname\")\n\n\t// Handle without Transport context\n\tsetClientSpan(ctx, span, nil)\n\n\t// Handle with proto message\n\tm := &binding.HelloRequest{}\n\tsetClientSpan(ctx, span, m)\n\n\t// Handle with metadata context\n\tctx = metadata.NewClientContext(ctx, metadata.New())\n\tsetClientSpan(ctx, span, m)\n\n\t// Handle with KindHTTP transport context\n\tmt := &mockTransport{\n\t\tkind: transport.KindHTTP,\n\t}\n\tmt.request, _ = http.NewRequest(http.MethodGet, \"/endpoint\", nil)\n\tmt.request.Host = \"MyServer\"\n\tctx = transport.NewClientContext(ctx, mt)\n\tsetClientSpan(ctx, span, m)\n\n\t// Handle with KindGRPC transport context\n\tmt.kind = transport.KindGRPC\n\tctx = transport.NewClientContext(ctx, mt)\n\tip, _ := net.ResolveIPAddr(\"ip\", \"1.1.1.1\")\n\tctx = peer.NewContext(ctx, &peer.Peer{\n\t\tAddr: ip,\n\t})\n\tsetClientSpan(ctx, span, m)\n\n\t// Handle without Host request\n\tctx = transport.NewClientContext(ctx, mt)\n\tsetClientSpan(ctx, span, m)\n}\n"
  },
  {
    "path": "middleware/tracing/statshandler.go",
    "content": "package tracing\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"go.opentelemetry.io/otel/trace\"\n\t\"google.golang.org/grpc/peer\"\n\t\"google.golang.org/grpc/stats\"\n)\n\n// ClientHandler is tracing ClientHandler\ntype ClientHandler struct{}\n\n// HandleConn exists to satisfy gRPC stats.Handler.\nfunc (c *ClientHandler) HandleConn(_ context.Context, _ stats.ConnStats) {\n\tfmt.Println(\"Handle connection.\")\n}\n\n// TagConn exists to satisfy gRPC stats.Handler.\nfunc (c *ClientHandler) TagConn(ctx context.Context, _ *stats.ConnTagInfo) context.Context {\n\treturn ctx\n}\n\n// HandleRPC implements per-RPC tracing and stats instrumentation.\nfunc (c *ClientHandler) HandleRPC(ctx context.Context, rs stats.RPCStats) {\n\tif _, ok := rs.(*stats.OutHeader); !ok {\n\t\treturn\n\t}\n\tp, ok := peer.FromContext(ctx)\n\tif !ok {\n\t\treturn\n\t}\n\tspan := trace.SpanFromContext(ctx)\n\tif span.SpanContext().IsValid() {\n\t\tspan.SetAttributes(peerAttr(p.Addr.String())...)\n\t}\n}\n\n// TagRPC implements per-RPC context management.\nfunc (c *ClientHandler) TagRPC(ctx context.Context, _ *stats.RPCTagInfo) context.Context {\n\treturn ctx\n}\n"
  },
  {
    "path": "middleware/tracing/statshandler_test.go",
    "content": "package tracing\n\nimport (\n\t\"context\"\n\t\"net\"\n\t\"testing\"\n\n\t\"go.opentelemetry.io/otel/trace\"\n\t\"go.opentelemetry.io/otel/trace/noop\"\n\t\"google.golang.org/grpc/peer\"\n\t\"google.golang.org/grpc/stats\"\n)\n\ntype ctxKey string\n\nconst testKey ctxKey = \"MY_TEST_KEY\"\n\nfunc TestClient_HandleConn(_ *testing.T) {\n\t(&ClientHandler{}).HandleConn(context.Background(), nil)\n}\n\nfunc TestClient_TagConn(t *testing.T) {\n\tclient := &ClientHandler{}\n\tctx := context.WithValue(context.Background(), testKey, 123)\n\n\tif client.TagConn(ctx, nil).Value(testKey) != 123 {\n\t\tt.Errorf(`The context value must be 123 for the \"MY_KEY_TEST\" key, %v given.`, client.TagConn(ctx, nil).Value(testKey))\n\t}\n}\n\nfunc TestClient_TagRPC(t *testing.T) {\n\tclient := &ClientHandler{}\n\tctx := context.WithValue(context.Background(), testKey, 123)\n\n\tif client.TagRPC(ctx, nil).Value(testKey) != 123 {\n\t\tt.Errorf(`The context value must be 123 for the \"MY_KEY_TEST\" key, %v given.`, client.TagConn(ctx, nil).Value(testKey))\n\t}\n}\n\ntype mockSpan struct {\n\ttrace.Span\n\tmockSpanCtx *trace.SpanContext\n}\n\nfunc (m *mockSpan) SpanContext() trace.SpanContext {\n\treturn *m.mockSpanCtx\n}\n\nfunc TestClient_HandleRPC(_ *testing.T) {\n\tclient := &ClientHandler{}\n\tctx := context.Background()\n\trs := stats.OutHeader{}\n\n\t// Handle stats.RPCStats is not type of stats.OutHeader case\n\tclient.HandleRPC(context.TODO(), nil)\n\n\t// Handle context doesn't have the peerkey filled with a Peer instance\n\tclient.HandleRPC(ctx, &rs)\n\n\t// Handle context with the peerkey filled with a Peer instance\n\tip, _ := net.ResolveIPAddr(\"ip\", \"1.1.1.1\")\n\tctx = peer.NewContext(ctx, &peer.Peer{\n\t\tAddr: ip,\n\t})\n\tclient.HandleRPC(ctx, &rs)\n\n\t// Handle context with Span\n\t_, span := noop.NewTracerProvider().Tracer(\"Tracer\").Start(ctx, \"Spanname\")\n\tspanCtx := trace.SpanContext{}\n\tspanID := [8]byte{12, 12, 12, 12, 12, 12, 12, 12}\n\ttraceID := [16]byte{12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12}\n\tspanCtx = spanCtx.WithTraceID(traceID)\n\tspanCtx = spanCtx.WithSpanID(spanID)\n\tmSpan := mockSpan{\n\t\tSpan:        span,\n\t\tmockSpanCtx: &spanCtx,\n\t}\n\tctx = trace.ContextWithSpan(ctx, &mSpan)\n\tclient.HandleRPC(ctx, &rs)\n}\n"
  },
  {
    "path": "middleware/tracing/tracer.go",
    "content": "package tracing\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"go.opentelemetry.io/otel\"\n\t\"go.opentelemetry.io/otel/attribute\"\n\t\"go.opentelemetry.io/otel/codes\"\n\t\"go.opentelemetry.io/otel/propagation\"\n\t\"go.opentelemetry.io/otel/trace\"\n\t\"google.golang.org/protobuf/proto\"\n\n\t\"github.com/go-kratos/kratos/v2/errors\"\n)\n\n// Tracer is otel span tracer\ntype Tracer struct {\n\ttracer trace.Tracer\n\tkind   trace.SpanKind\n\topt    *options\n}\n\n// NewTracer create tracer instance\nfunc NewTracer(kind trace.SpanKind, opts ...Option) *Tracer {\n\top := options{\n\t\tpropagator: propagation.NewCompositeTextMapPropagator(Metadata{}, propagation.Baggage{}, propagation.TraceContext{}),\n\t\ttracerName: \"kratos\",\n\t}\n\tfor _, o := range opts {\n\t\to(&op)\n\t}\n\tif op.tracerProvider == nil {\n\t\top.tracerProvider = otel.GetTracerProvider()\n\t}\n\n\tswitch kind {\n\tcase trace.SpanKindClient:\n\t\treturn &Tracer{tracer: op.tracerProvider.Tracer(op.tracerName), kind: kind, opt: &op}\n\tcase trace.SpanKindServer:\n\t\treturn &Tracer{tracer: op.tracerProvider.Tracer(op.tracerName), kind: kind, opt: &op}\n\tdefault:\n\t\tpanic(fmt.Sprintf(\"unsupported span kind: %v\", kind))\n\t}\n}\n\n// Start start tracing span\nfunc (t *Tracer) Start(ctx context.Context, operation string, carrier propagation.TextMapCarrier) (context.Context, trace.Span) {\n\tif t.kind == trace.SpanKindServer {\n\t\tctx = t.opt.propagator.Extract(ctx, carrier)\n\t}\n\tctx, span := t.tracer.Start(ctx,\n\t\toperation,\n\t\ttrace.WithSpanKind(t.kind),\n\t)\n\tif t.kind == trace.SpanKindClient {\n\t\tt.opt.propagator.Inject(ctx, carrier)\n\t}\n\treturn ctx, span\n}\n\n// End finish tracing span\nfunc (t *Tracer) End(_ context.Context, span trace.Span, m any, err error) {\n\tif err != nil {\n\t\tspan.RecordError(err)\n\t\tif e := errors.FromError(err); e != nil {\n\t\t\tspan.SetAttributes(attribute.Key(\"rpc.status_code\").Int64(int64(e.Code)))\n\t\t}\n\t\tspan.SetStatus(codes.Error, err.Error())\n\t} else {\n\t\tspan.SetStatus(codes.Ok, \"OK\")\n\t}\n\n\tif p, ok := m.(proto.Message); ok {\n\t\tif t.kind == trace.SpanKindServer {\n\t\t\tspan.SetAttributes(attribute.Key(\"send_msg.size\").Int(proto.Size(p)))\n\t\t} else {\n\t\t\tspan.SetAttributes(attribute.Key(\"recv_msg.size\").Int(proto.Size(p)))\n\t\t}\n\t}\n\tspan.End()\n}\n"
  },
  {
    "path": "middleware/tracing/tracer_test.go",
    "content": "package tracing\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"testing\"\n\n\t\"go.opentelemetry.io/otel/trace\"\n\t\"go.opentelemetry.io/otel/trace/noop\"\n\n\t\"github.com/go-kratos/kratos/v2/internal/testdata/binding\"\n)\n\nfunc TestNewTracer(t *testing.T) {\n\ttracer := NewTracer(trace.SpanKindClient, func(o *options) {\n\t\to.tracerProvider = noop.NewTracerProvider()\n\t})\n\n\tif tracer.kind != trace.SpanKindClient {\n\t\tt.Errorf(\"The tracer kind must be equal to trace.SpanKindClient, %v given.\", tracer.kind)\n\t}\n\n\tdefer func() {\n\t\tif recover() == nil {\n\t\t\tt.Error(\"The NewTracer with an invalid SpanKindMustCrash must panic\")\n\t\t}\n\t}()\n\t_ = NewTracer(666, func(o *options) {\n\t\to.tracerProvider = noop.NewTracerProvider()\n\t})\n}\n\nfunc TestTracer_End(_ *testing.T) {\n\ttracer := NewTracer(trace.SpanKindClient, func(o *options) {\n\t\to.tracerProvider = noop.NewTracerProvider()\n\t})\n\tctx, span := noop.NewTracerProvider().Tracer(\"noop\").Start(context.Background(), \"noopSpan\")\n\n\t// Handle with error case\n\ttracer.End(ctx, span, nil, errors.New(\"dummy error\"))\n\n\t// Handle without error case\n\ttracer.End(ctx, span, nil, nil)\n\n\tm := &binding.HelloRequest{}\n\n\t// Handle the trace KindServer\n\ttracer = NewTracer(trace.SpanKindServer, func(o *options) {\n\t\to.tracerProvider = noop.NewTracerProvider()\n\t})\n\ttracer.End(ctx, span, m, nil)\n\ttracer = NewTracer(trace.SpanKindClient, func(o *options) {\n\t\to.tracerProvider = noop.NewTracerProvider()\n\t})\n\ttracer.End(ctx, span, m, nil)\n}\n"
  },
  {
    "path": "middleware/tracing/tracing.go",
    "content": "package tracing\n\nimport (\n\t\"context\"\n\n\t\"github.com/go-kratos/kratos/v2/log\"\n\n\t\"go.opentelemetry.io/otel/propagation\"\n\t\"go.opentelemetry.io/otel/trace\"\n\n\t\"github.com/go-kratos/kratos/v2/middleware\"\n\t\"github.com/go-kratos/kratos/v2/transport\"\n)\n\n// Option is tracing option.\ntype Option func(*options)\n\ntype options struct {\n\ttracerName     string\n\ttracerProvider trace.TracerProvider\n\tpropagator     propagation.TextMapPropagator\n}\n\n// WithPropagator with tracer propagator.\nfunc WithPropagator(propagator propagation.TextMapPropagator) Option {\n\treturn func(opts *options) {\n\t\topts.propagator = propagator\n\t}\n}\n\n// WithTracerProvider with tracer provider.\n// By default, it uses the global provider that is set by otel.SetTracerProvider(provider).\nfunc WithTracerProvider(provider trace.TracerProvider) Option {\n\treturn func(opts *options) {\n\t\topts.tracerProvider = provider\n\t}\n}\n\n// WithTracerName with tracer name\nfunc WithTracerName(tracerName string) Option {\n\treturn func(opts *options) {\n\t\topts.tracerName = tracerName\n\t}\n}\n\n// Server returns a new server middleware for OpenTelemetry.\nfunc Server(opts ...Option) middleware.Middleware {\n\ttracer := NewTracer(trace.SpanKindServer, opts...)\n\treturn func(handler middleware.Handler) middleware.Handler {\n\t\treturn func(ctx context.Context, req any) (reply any, err error) {\n\t\t\tif tr, ok := transport.FromServerContext(ctx); ok {\n\t\t\t\tvar span trace.Span\n\t\t\t\tctx, span = tracer.Start(ctx, tr.Operation(), tr.RequestHeader())\n\t\t\t\tsetServerSpan(ctx, span, req)\n\t\t\t\tdefer func() { tracer.End(ctx, span, reply, err) }()\n\t\t\t}\n\t\t\treturn handler(ctx, req)\n\t\t}\n\t}\n}\n\n// Client returns a new client middleware for OpenTelemetry.\nfunc Client(opts ...Option) middleware.Middleware {\n\ttracer := NewTracer(trace.SpanKindClient, opts...)\n\treturn func(handler middleware.Handler) middleware.Handler {\n\t\treturn func(ctx context.Context, req any) (reply any, err error) {\n\t\t\tif tr, ok := transport.FromClientContext(ctx); ok {\n\t\t\t\tvar span trace.Span\n\t\t\t\tctx, span = tracer.Start(ctx, tr.Operation(), tr.RequestHeader())\n\t\t\t\tsetClientSpan(ctx, span, req)\n\t\t\t\tdefer func() { tracer.End(ctx, span, reply, err) }()\n\t\t\t}\n\t\t\treturn handler(ctx, req)\n\t\t}\n\t}\n}\n\n// TraceID returns a traceid valuer.\nfunc TraceID() log.Valuer {\n\treturn func(ctx context.Context) any {\n\t\tif span := trace.SpanContextFromContext(ctx); span.HasTraceID() {\n\t\t\treturn span.TraceID().String()\n\t\t}\n\t\treturn \"\"\n\t}\n}\n\n// SpanID returns a spanid valuer.\nfunc SpanID() log.Valuer {\n\treturn func(ctx context.Context) any {\n\t\tif span := trace.SpanContextFromContext(ctx); span.HasSpanID() {\n\t\t\treturn span.SpanID().String()\n\t\t}\n\t\treturn \"\"\n\t}\n}\n"
  },
  {
    "path": "middleware/tracing/tracing_test.go",
    "content": "package tracing\n\nimport (\n\t\"context\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"os\"\n\t\"reflect\"\n\t\"testing\"\n\n\t\"go.opentelemetry.io/otel/propagation\"\n\ttracesdk \"go.opentelemetry.io/otel/sdk/trace\"\n\t\"go.opentelemetry.io/otel/trace\"\n\n\t\"github.com/go-kratos/kratos/v2/log\"\n\t\"github.com/go-kratos/kratos/v2/transport\"\n)\n\nvar _ transport.Transporter = (*mockTransport)(nil)\n\ntype headerCarrier http.Header\n\n// Get returns the value associated with the passed key.\nfunc (hc headerCarrier) Get(key string) string {\n\treturn http.Header(hc).Get(key)\n}\n\n// Set stores the key-value pair.\nfunc (hc headerCarrier) Set(key string, value string) {\n\thttp.Header(hc).Set(key, value)\n}\n\n// Add value to the key-value pair.\nfunc (hc headerCarrier) Add(key string, value string) {\n\thttp.Header(hc).Add(key, value)\n}\n\n// Keys lists the keys stored in this carrier.\nfunc (hc headerCarrier) Keys() []string {\n\tkeys := make([]string, 0, len(hc))\n\tfor k := range http.Header(hc) {\n\t\tkeys = append(keys, k)\n\t}\n\treturn keys\n}\n\n// Values returns a slice value associated with the passed key.\nfunc (hc headerCarrier) Values(key string) []string {\n\treturn http.Header(hc).Values(key)\n}\n\ntype mockTransport struct {\n\tkind      transport.Kind\n\tendpoint  string\n\toperation string\n\theader    headerCarrier\n\trequest   *http.Request\n}\n\nfunc (tr *mockTransport) Kind() transport.Kind            { return tr.kind }\nfunc (tr *mockTransport) Endpoint() string                { return tr.endpoint }\nfunc (tr *mockTransport) Operation() string               { return tr.operation }\nfunc (tr *mockTransport) RequestHeader() transport.Header { return tr.header }\nfunc (tr *mockTransport) ReplyHeader() transport.Header   { return tr.header }\nfunc (tr *mockTransport) Request() *http.Request {\n\tif tr.request == nil {\n\t\trq, _ := http.NewRequest(http.MethodGet, \"/endpoint\", nil)\n\n\t\treturn rq\n\t}\n\n\treturn tr.request\n}\nfunc (tr *mockTransport) PathTemplate() string { return \"\" }\nfunc (tr *mockTransport) Response() http.ResponseWriter {\n\treturn httptest.NewRecorder()\n}\n\nfunc TestTracer(t *testing.T) {\n\tcarrier := headerCarrier{}\n\ttp := tracesdk.NewTracerProvider(tracesdk.WithSampler(tracesdk.TraceIDRatioBased(0)))\n\n\t// caller use Inject\n\tcliTracer := NewTracer(\n\t\ttrace.SpanKindClient,\n\t\tWithTracerProvider(tp),\n\t\tWithPropagator(\n\t\t\tpropagation.NewCompositeTextMapPropagator(propagation.Baggage{}, propagation.TraceContext{}),\n\t\t),\n\t)\n\n\tts := &mockTransport{kind: transport.KindHTTP, header: carrier}\n\n\tctx, aboveSpan := cliTracer.Start(transport.NewClientContext(context.Background(), ts), ts.Operation(), ts.RequestHeader())\n\tdefer cliTracer.End(ctx, aboveSpan, nil, nil)\n\n\t// server use Extract fetch traceInfo from carrier\n\tsvrTracer := NewTracer(trace.SpanKindServer, WithPropagator(propagation.NewCompositeTextMapPropagator(propagation.Baggage{}, propagation.TraceContext{})))\n\tts = &mockTransport{kind: transport.KindHTTP, header: carrier}\n\n\tctx, span := svrTracer.Start(transport.NewServerContext(ctx, ts), ts.Operation(), ts.RequestHeader())\n\tdefer svrTracer.End(ctx, span, nil, nil)\n\n\tif aboveSpan.SpanContext().TraceID() != span.SpanContext().TraceID() {\n\t\tt.Fatalf(\"TraceID failed to deliver\")\n\t}\n\n\tif v, ok := transport.FromClientContext(ctx); !ok || len(v.RequestHeader().Keys()) == 0 {\n\t\tt.Fatalf(\"traceHeader failed to deliver\")\n\t}\n}\n\nfunc TestServer(t *testing.T) {\n\ttr := &mockTransport{\n\t\tkind:      transport.KindHTTP,\n\t\tendpoint:  \"server:2233\",\n\t\toperation: \"/test.server/hello\",\n\t\theader:    headerCarrier{},\n\t}\n\n\ttracer := NewTracer(\n\t\ttrace.SpanKindClient,\n\t\tWithTracerProvider(tracesdk.NewTracerProvider()),\n\t)\n\n\tlogger := log.NewStdLogger(os.Stdout)\n\tlogger = log.With(logger, \"span_id\", SpanID())\n\tlogger = log.With(logger, \"trace_id\", TraceID())\n\n\tvar (\n\t\tchildSpanID  string\n\t\tchildTraceID string\n\t)\n\tnext := func(ctx context.Context, req any) (any, error) {\n\t\t_ = log.WithContext(ctx, logger).Log(log.LevelInfo,\n\t\t\t\"kind\", \"server\",\n\t\t)\n\t\tchildSpanID = SpanID()(ctx).(string)\n\t\tchildTraceID = TraceID()(ctx).(string)\n\t\treturn req.(string) + \"https://go-kratos.dev\", nil\n\t}\n\n\tvar ctx context.Context\n\tctx, span := tracer.Start(\n\t\ttransport.NewServerContext(context.Background(), tr),\n\t\ttr.Operation(),\n\t\ttr.RequestHeader(),\n\t)\n\n\t_, err := Server(\n\t\tWithTracerProvider(tracesdk.NewTracerProvider()),\n\t\tWithPropagator(propagation.NewCompositeTextMapPropagator(propagation.Baggage{}, propagation.TraceContext{})),\n\t)(next)(ctx, \"test server: \")\n\n\tspan.End()\n\tif err != nil {\n\t\tt.Errorf(\"expected nil, got %v\", err)\n\t}\n\tif childSpanID == \"\" {\n\t\tt.Errorf(\"expected empty, got %v\", childSpanID)\n\t}\n\tif reflect.DeepEqual(span.SpanContext().SpanID().String(), childSpanID) {\n\t\tt.Errorf(\"span.SpanContext().SpanID().String()(%v) is not equal to childSpanID(%v)\", span.SpanContext().SpanID().String(), childSpanID)\n\t}\n\tif !reflect.DeepEqual(span.SpanContext().TraceID().String(), childTraceID) {\n\t\tt.Errorf(\"expected %v, got %v\", childTraceID, span.SpanContext().TraceID().String())\n\t}\n\n\t_, err = Server(\n\t\tWithTracerProvider(tracesdk.NewTracerProvider()),\n\t\tWithPropagator(propagation.NewCompositeTextMapPropagator(propagation.Baggage{}, propagation.TraceContext{})),\n\t)(next)(context.Background(), \"test server: \")\n\tif err != nil {\n\t\tt.Errorf(\"expected error, got nil\")\n\t}\n\tif childSpanID != \"\" {\n\t\tt.Errorf(\"expected empty, got %v\", childSpanID)\n\t}\n\tif childTraceID != \"\" {\n\t\tt.Errorf(\"expected empty, got %v\", childTraceID)\n\t}\n}\n\nfunc TestClient(t *testing.T) {\n\ttr := &mockTransport{\n\t\tkind:      transport.KindHTTP,\n\t\tendpoint:  \"server:2233\",\n\t\toperation: \"/test.server/hello\",\n\t\theader:    headerCarrier{},\n\t}\n\n\ttracer := NewTracer(\n\t\ttrace.SpanKindClient,\n\t\tWithTracerProvider(tracesdk.NewTracerProvider()),\n\t)\n\n\tlogger := log.NewStdLogger(os.Stdout)\n\tlogger = log.With(logger, \"span_id\", SpanID())\n\tlogger = log.With(logger, \"trace_id\", TraceID())\n\n\tvar (\n\t\tchildSpanID  string\n\t\tchildTraceID string\n\t)\n\tnext := func(ctx context.Context, req any) (any, error) {\n\t\t_ = log.WithContext(ctx, logger).Log(log.LevelInfo,\n\t\t\t\"kind\", \"client\",\n\t\t)\n\t\tchildSpanID = SpanID()(ctx).(string)\n\t\tchildTraceID = TraceID()(ctx).(string)\n\t\treturn req.(string) + \"https://go-kratos.dev\", nil\n\t}\n\n\tvar ctx context.Context\n\tctx, span := tracer.Start(\n\t\ttransport.NewClientContext(context.Background(), tr),\n\t\ttr.Operation(),\n\t\ttr.RequestHeader(),\n\t)\n\n\t_, err := Client(\n\t\tWithTracerProvider(tracesdk.NewTracerProvider()),\n\t\tWithPropagator(propagation.NewCompositeTextMapPropagator(propagation.Baggage{}, propagation.TraceContext{})),\n\t)(next)(ctx, \"test client: \")\n\n\tspan.End()\n\tif err != nil {\n\t\tt.Errorf(\"expected nil, got %v\", err)\n\t}\n\tif childSpanID == \"\" {\n\t\tt.Errorf(\"expected empty, got %v\", childSpanID)\n\t}\n\tif reflect.DeepEqual(span.SpanContext().SpanID().String(), childSpanID) {\n\t\tt.Errorf(\"span.SpanContext().SpanID().String()(%v) is not equal to childSpanID(%v)\", span.SpanContext().SpanID().String(), childSpanID)\n\t}\n\tif !reflect.DeepEqual(span.SpanContext().TraceID().String(), childTraceID) {\n\t\tt.Errorf(\"expected %v, got %v\", childTraceID, span.SpanContext().TraceID().String())\n\t}\n}\n"
  },
  {
    "path": "middleware/validate/validate.go",
    "content": "package validate\n\nimport (\n\t\"context\"\n\n\t\"github.com/go-kratos/kratos/v2/errors\"\n\t\"github.com/go-kratos/kratos/v2/middleware\"\n)\n\n// ValidatorFunc defines a validation function type.\ntype ValidatorFunc func(v any) error\n\n// validator is an interface for types that can validate themselves.\ntype validator interface {\n\tValidate() error\n}\n\n// Validator returns a middleware that performs validation on requests.\n// Example usage:\n//\n// buf validate(https://github.com/bufbuild/protovalidate):\n// import \"buf.build/go/protovalidate\"\n//\n//\tValidator(func(v any) error {\n//\t    if msg, ok := req.(proto.Message); ok {\n//\t        if err := protovalidate.Validate(msg); err != nil {\n//\t            return nil, err\n//\t\t\t}\n//\t\t}\n//\t    return nil\n//\t})\n//\n// Google AIP field behavior validate(https://google.aip.dev/203):\n// import \"go.einride.tech/aip/fieldbehavior\"\n//\n//\tValidator(func(v any) error {\n//\t    if msg, ok := req.(proto.Message); ok {\n//\t\t    if err := fieldbehavior.ValidateRequiredFields(msg); err != nil {\n//\t\t        return nil, err\n//\t\t    }\n//\t\t}\n//\t    return nil\n//\t})\nfunc Validator(validators ...ValidatorFunc) middleware.Middleware {\n\treturn func(handler middleware.Handler) middleware.Handler {\n\t\treturn func(ctx context.Context, req any) (reply any, err error) {\n\t\t\tif v, ok := req.(validator); ok {\n\t\t\t\tif err := v.Validate(); err != nil {\n\t\t\t\t\treturn nil, errors.BadRequest(\"VALIDATOR\", err.Error()).WithCause(err)\n\t\t\t\t}\n\t\t\t}\n\t\t\tfor _, v := range validators {\n\t\t\t\tif err := v(req); err != nil {\n\t\t\t\t\treturn nil, errors.BadRequest(\"VALIDATOR\", err.Error()).WithCause(err)\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn handler(ctx, req)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "middleware/validate/validate_test.go",
    "content": "package validate\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"testing\"\n\n\tkratoserrors \"github.com/go-kratos/kratos/v2/errors\"\n\t\"github.com/go-kratos/kratos/v2/middleware\"\n)\n\n// protoVali implement validate.validator\ntype protoVali struct {\n\tname  string\n\tage   int\n\tisErr bool\n}\n\nfunc (v protoVali) Validate() error {\n\tif v.name == \"\" || v.age < 0 {\n\t\treturn errors.New(\"err\")\n\t}\n\treturn nil\n}\n\nfunc TestTable(t *testing.T) {\n\tvar mock middleware.Handler = func(context.Context, any) (any, error) { return nil, nil }\n\n\ttests := []protoVali{\n\t\t{\"v1\", 365, false},\n\t\t{\"v2\", -1, true},\n\t\t{\"\", 365, true},\n\t}\n\tfor _, test := range tests {\n\t\tt.Run(test.name, func(t *testing.T) {\n\t\t\tv := Validator()(mock)\n\t\t\t_, err := v(context.Background(), test)\n\t\t\tif want, have := test.isErr, kratoserrors.IsBadRequest(err); want != have {\n\t\t\t\tt.Errorf(\"fail data %v, want %v, have %v\", test, want, have)\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "options.go",
    "content": "package kratos\n\nimport (\n\t\"context\"\n\t\"net/url\"\n\t\"os\"\n\t\"time\"\n\n\t\"github.com/go-kratos/kratos/v2/log\"\n\t\"github.com/go-kratos/kratos/v2/registry\"\n\t\"github.com/go-kratos/kratos/v2/transport\"\n)\n\n// Option is an application option.\ntype Option func(o *options)\n\n// options is an application options.\ntype options struct {\n\tid        string\n\tname      string\n\tversion   string\n\tmetadata  map[string]string\n\tendpoints []*url.URL\n\n\tctx  context.Context\n\tsigs []os.Signal\n\n\tlogger           log.Logger\n\tregistrar        registry.Registrar\n\tregistrarTimeout time.Duration\n\tstopTimeout      time.Duration\n\tservers          []transport.Server\n\n\t// Before and After funcs\n\tbeforeStart []func(context.Context) error\n\tbeforeStop  []func(context.Context) error\n\tafterStart  []func(context.Context) error\n\tafterStop   []func(context.Context) error\n}\n\n// ID with service id.\nfunc ID(id string) Option {\n\treturn func(o *options) { o.id = id }\n}\n\n// Name with service name.\nfunc Name(name string) Option {\n\treturn func(o *options) { o.name = name }\n}\n\n// Version with service version.\nfunc Version(version string) Option {\n\treturn func(o *options) { o.version = version }\n}\n\n// Metadata with service metadata.\nfunc Metadata(md map[string]string) Option {\n\treturn func(o *options) { o.metadata = md }\n}\n\n// Endpoint with service endpoint.\nfunc Endpoint(endpoints ...*url.URL) Option {\n\treturn func(o *options) { o.endpoints = endpoints }\n}\n\n// Context with service context.\nfunc Context(ctx context.Context) Option {\n\treturn func(o *options) { o.ctx = ctx }\n}\n\n// Logger with service logger.\nfunc Logger(logger log.Logger) Option {\n\treturn func(o *options) { o.logger = logger }\n}\n\n// Server with transport servers.\nfunc Server(srv ...transport.Server) Option {\n\treturn func(o *options) { o.servers = srv }\n}\n\n// Signal with exit signals.\nfunc Signal(sigs ...os.Signal) Option {\n\treturn func(o *options) { o.sigs = sigs }\n}\n\n// Registrar with service registry.\nfunc Registrar(r registry.Registrar) Option {\n\treturn func(o *options) { o.registrar = r }\n}\n\n// RegistrarTimeout with registrar timeout.\nfunc RegistrarTimeout(t time.Duration) Option {\n\treturn func(o *options) { o.registrarTimeout = t }\n}\n\n// StopTimeout with app stop timeout.\nfunc StopTimeout(t time.Duration) Option {\n\treturn func(o *options) { o.stopTimeout = t }\n}\n\n// Before and Afters\n\n// BeforeStart run funcs before app starts\nfunc BeforeStart(fn func(context.Context) error) Option {\n\treturn func(o *options) {\n\t\to.beforeStart = append(o.beforeStart, fn)\n\t}\n}\n\n// BeforeStop run funcs before app stops\nfunc BeforeStop(fn func(context.Context) error) Option {\n\treturn func(o *options) {\n\t\to.beforeStop = append(o.beforeStop, fn)\n\t}\n}\n\n// AfterStart run funcs after app starts\nfunc AfterStart(fn func(context.Context) error) Option {\n\treturn func(o *options) {\n\t\to.afterStart = append(o.afterStart, fn)\n\t}\n}\n\n// AfterStop run funcs after app stops\nfunc AfterStop(fn func(context.Context) error) Option {\n\treturn func(o *options) {\n\t\to.afterStop = append(o.afterStop, fn)\n\t}\n}\n"
  },
  {
    "path": "options_test.go",
    "content": "package kratos\n\nimport (\n\t\"context\"\n\t\"log\"\n\t\"net/url\"\n\t\"os\"\n\t\"reflect\"\n\t\"testing\"\n\t\"time\"\n\n\txlog \"github.com/go-kratos/kratos/v2/log\"\n\t\"github.com/go-kratos/kratos/v2/registry\"\n\t\"github.com/go-kratos/kratos/v2/transport\"\n)\n\nfunc TestID(t *testing.T) {\n\to := &options{}\n\tv := \"123\"\n\tID(v)(o)\n\tif !reflect.DeepEqual(v, o.id) {\n\t\tt.Fatalf(\"o.id:%s is not equal to v:%s\", o.id, v)\n\t}\n}\n\nfunc TestName(t *testing.T) {\n\to := &options{}\n\tv := \"abc\"\n\tName(v)(o)\n\tif !reflect.DeepEqual(v, o.name) {\n\t\tt.Fatalf(\"o.name:%s is not equal to v:%s\", o.name, v)\n\t}\n}\n\nfunc TestVersion(t *testing.T) {\n\to := &options{}\n\tv := \"123\"\n\tVersion(v)(o)\n\tif !reflect.DeepEqual(v, o.version) {\n\t\tt.Fatalf(\"o.version:%s is not equal to v:%s\", o.version, v)\n\t}\n}\n\nfunc TestMetadata(t *testing.T) {\n\to := &options{}\n\tv := map[string]string{\n\t\t\"a\": \"1\",\n\t\t\"b\": \"2\",\n\t}\n\tMetadata(v)(o)\n\tif !reflect.DeepEqual(v, o.metadata) {\n\t\tt.Fatalf(\"o.metadata:%s is not equal to v:%s\", o.metadata, v)\n\t}\n}\n\nfunc TestEndpoint(t *testing.T) {\n\to := &options{}\n\tv := []*url.URL{\n\t\t{Host: \"example.com\"},\n\t\t{Host: \"foo.com\"},\n\t}\n\tEndpoint(v...)(o)\n\tif !reflect.DeepEqual(v, o.endpoints) {\n\t\tt.Fatalf(\"o.endpoints:%s is not equal to v:%s\", o.endpoints, v)\n\t}\n}\n\nfunc TestContext(t *testing.T) {\n\ttype ctxKey struct {\n\t\tKey string\n\t}\n\to := &options{}\n\tv := context.WithValue(context.TODO(), ctxKey{Key: \"context\"}, \"b\")\n\tContext(v)(o)\n\tif !reflect.DeepEqual(v, o.ctx) {\n\t\tt.Fatalf(\"o.ctx:%s is not equal to v:%s\", o.ctx, v)\n\t}\n}\n\nfunc TestLogger(t *testing.T) {\n\to := &options{}\n\tv := xlog.NewStdLogger(log.Writer())\n\tLogger(v)(o)\n\tif !reflect.DeepEqual(v, o.logger) {\n\t\tt.Fatalf(\"o.logger:%v is not equal to xlog.NewHelper(v):%v\", o.logger, xlog.NewHelper(v))\n\t}\n}\n\ntype mockServer struct {\n\tstopFn func(context.Context) error\n}\n\nfunc (m *mockServer) Start(_ context.Context) error { return nil }\nfunc (m *mockServer) Stop(ctx context.Context) error {\n\tif m.stopFn != nil {\n\t\treturn m.stopFn(ctx)\n\t}\n\treturn nil\n}\n\nfunc TestServer(t *testing.T) {\n\to := &options{}\n\tv := []transport.Server{\n\t\t&mockServer{}, &mockServer{},\n\t}\n\tServer(v...)(o)\n\tif !reflect.DeepEqual(v, o.servers) {\n\t\tt.Fatalf(\"o.servers:%s is not equal to xlog.NewHelper(v):%s\", o.servers, v)\n\t}\n}\n\ntype mockSignal struct{}\n\nfunc (m *mockSignal) String() string { return \"sig\" }\nfunc (m *mockSignal) Signal()        {}\n\nfunc TestSignal(t *testing.T) {\n\to := &options{}\n\tv := []os.Signal{\n\t\t&mockSignal{}, &mockSignal{},\n\t}\n\tSignal(v...)(o)\n\tif !reflect.DeepEqual(v, o.sigs) {\n\t\tt.Fatal(\"o.sigs is not equal to v\")\n\t}\n}\n\ntype mockRegistrar struct{}\n\nfunc (m *mockRegistrar) Register(_ context.Context, _ *registry.ServiceInstance) error {\n\treturn nil\n}\n\nfunc (m *mockRegistrar) Deregister(_ context.Context, _ *registry.ServiceInstance) error {\n\treturn nil\n}\n\nfunc TestRegistrar(t *testing.T) {\n\to := &options{}\n\tv := &mockRegistrar{}\n\tRegistrar(v)(o)\n\tif !reflect.DeepEqual(v, o.registrar) {\n\t\tt.Fatal(\"o.registrar is not equal to v\")\n\t}\n}\n\nfunc TestRegistrarTimeout(t *testing.T) {\n\to := &options{}\n\tv := time.Duration(123)\n\tRegistrarTimeout(v)(o)\n\tif !reflect.DeepEqual(v, o.registrarTimeout) {\n\t\tt.Fatal(\"o.registrarTimeout is not equal to v\")\n\t}\n}\n\nfunc TestStopTimeout(t *testing.T) {\n\to := &options{}\n\tv := time.Duration(123)\n\tStopTimeout(v)(o)\n\tif !reflect.DeepEqual(v, o.stopTimeout) {\n\t\tt.Fatal(\"o.stopTimeout is not equal to v\")\n\t}\n}\n\nfunc TestBeforeStart(t *testing.T) {\n\to := &options{}\n\tv := func(_ context.Context) error {\n\t\tt.Log(\"BeforeStart...\")\n\t\treturn nil\n\t}\n\tBeforeStart(v)(o)\n}\n\nfunc TestBeforeStop(t *testing.T) {\n\to := &options{}\n\tv := func(_ context.Context) error {\n\t\tt.Log(\"BeforeStop...\")\n\t\treturn nil\n\t}\n\tBeforeStop(v)(o)\n}\n\nfunc TestAfterStart(t *testing.T) {\n\to := &options{}\n\tv := func(_ context.Context) error {\n\t\tt.Log(\"AfterStart...\")\n\t\treturn nil\n\t}\n\tAfterStart(v)(o)\n}\n\nfunc TestAfterStop(t *testing.T) {\n\to := &options{}\n\tv := func(_ context.Context) error {\n\t\tt.Log(\"AfterStop...\")\n\t\treturn nil\n\t}\n\tAfterStop(v)(o)\n}\n"
  },
  {
    "path": "registry/README.md",
    "content": "# Registry\n\n## Consul\n\n```shell\ngo get -u github.com/go-kratos/kratos/contrib/registry/consul/v2\n```\n\n## Etcd\n\n```shell\ngo get -u github.com/go-kratos/kratos/contrib/registry/etcd/v2\n```\n\n## zookeeper\n\n```shell\ngo get -u github.com/go-kratos/kratos/contrib/registry/zookeeper/v2\n```\n\n## Nacos\n\n```shell\ngo get -u github.com/go-kratos/kratos/contrib/registry/nacos/v2\n```\n\n## kubernetes\n\n```shell\ngo get -u github.com/go-kratos/kratos/contrib/registry/kubernetes/v2\n```\n\n## polaris\n\n```shell\ngo get -u github.com/go-kratos/kratos/contrib/registry/polaris/v2\n```\n"
  },
  {
    "path": "registry/registry.go",
    "content": "package registry\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"sort\"\n)\n\n// Registrar is service registrar.\ntype Registrar interface {\n\t// Register the registration.\n\tRegister(ctx context.Context, service *ServiceInstance) error\n\t// Deregister the registration.\n\tDeregister(ctx context.Context, service *ServiceInstance) error\n}\n\n// Discovery is service discovery.\ntype Discovery interface {\n\t// GetService return the service instances in memory according to the service name.\n\tGetService(ctx context.Context, serviceName string) ([]*ServiceInstance, error)\n\t// Watch creates a watcher according to the service name.\n\tWatch(ctx context.Context, serviceName string) (Watcher, error)\n}\n\n// Watcher is service watcher.\ntype Watcher interface {\n\t// Next returns services in the following two cases:\n\t// 1.the first time to watch and the service instance list is not empty.\n\t// 2.any service instance changes found.\n\t// if the above two conditions are not met, it will block until context deadline exceeded or canceled\n\tNext() ([]*ServiceInstance, error)\n\t// Stop close the watcher.\n\tStop() error\n}\n\n// ServiceInstance is an instance of a service in a discovery system.\ntype ServiceInstance struct {\n\t// ID is the unique instance ID as registered.\n\tID string `json:\"id\"`\n\t// Name is the service name as registered.\n\tName string `json:\"name\"`\n\t// Version is the version of the compiled.\n\tVersion string `json:\"version\"`\n\t// Metadata is the kv pair metadata associated with the service instance.\n\tMetadata map[string]string `json:\"metadata\"`\n\t// Endpoints are endpoint addresses of the service instance.\n\t// schema:\n\t//   http://127.0.0.1:8000?isSecure=false\n\t//   grpc://127.0.0.1:9000?isSecure=false\n\tEndpoints []string `json:\"endpoints\"`\n}\n\nfunc (i *ServiceInstance) String() string {\n\treturn fmt.Sprintf(\"%s-%s\", i.Name, i.ID)\n}\n\n// Equal returns whether i and o are equivalent.\nfunc (i *ServiceInstance) Equal(o any) bool {\n\tif i == nil && o == nil {\n\t\treturn true\n\t}\n\n\tif i == nil || o == nil {\n\t\treturn false\n\t}\n\n\tt, ok := o.(*ServiceInstance)\n\tif !ok {\n\t\treturn false\n\t}\n\n\tif len(i.Endpoints) != len(t.Endpoints) {\n\t\treturn false\n\t}\n\n\tsort.Strings(i.Endpoints)\n\tsort.Strings(t.Endpoints)\n\tfor j := 0; j < len(i.Endpoints); j++ {\n\t\tif i.Endpoints[j] != t.Endpoints[j] {\n\t\t\treturn false\n\t\t}\n\t}\n\n\tif len(i.Metadata) != len(t.Metadata) {\n\t\treturn false\n\t}\n\n\tfor k, v := range i.Metadata {\n\t\tif v != t.Metadata[k] {\n\t\t\treturn false\n\t\t}\n\t}\n\n\treturn i.ID == t.ID && i.Name == t.Name && i.Version == t.Version\n}\n"
  },
  {
    "path": "selector/balancer.go",
    "content": "package selector\n\nimport (\n\t\"context\"\n\t\"time\"\n)\n\n// Balancer is balancer interface\ntype Balancer interface {\n\tPick(ctx context.Context, nodes []WeightedNode) (selected WeightedNode, done DoneFunc, err error)\n}\n\n// BalancerBuilder build balancer\ntype BalancerBuilder interface {\n\tBuild() Balancer\n}\n\n// WeightedNode calculates scheduling weight in real time\ntype WeightedNode interface {\n\tNode\n\n\t// Raw returns the original node\n\tRaw() Node\n\n\t// Weight is the runtime calculated weight\n\tWeight() float64\n\n\t// Pick the node\n\tPick() DoneFunc\n\n\t// PickElapsed is time elapsed since the latest pick\n\tPickElapsed() time.Duration\n}\n\n// WeightedNodeBuilder is WeightedNode Builder\ntype WeightedNodeBuilder interface {\n\tBuild(Node) WeightedNode\n}\n"
  },
  {
    "path": "selector/default_node.go",
    "content": "package selector\n\nimport (\n\t\"strconv\"\n\n\t\"github.com/go-kratos/kratos/v2/registry\"\n)\n\nvar _ Node = (*DefaultNode)(nil)\n\n// DefaultNode is selector node\ntype DefaultNode struct {\n\tscheme   string\n\taddr     string\n\tweight   *int64\n\tversion  string\n\tname     string\n\tmetadata map[string]string\n}\n\n// Scheme is node scheme\nfunc (n *DefaultNode) Scheme() string {\n\treturn n.scheme\n}\n\n// Address is node address\nfunc (n *DefaultNode) Address() string {\n\treturn n.addr\n}\n\n// ServiceName is node serviceName\nfunc (n *DefaultNode) ServiceName() string {\n\treturn n.name\n}\n\n// InitialWeight is node initialWeight\nfunc (n *DefaultNode) InitialWeight() *int64 {\n\treturn n.weight\n}\n\n// Version is node version\nfunc (n *DefaultNode) Version() string {\n\treturn n.version\n}\n\n// Metadata is node metadata\nfunc (n *DefaultNode) Metadata() map[string]string {\n\treturn n.metadata\n}\n\n// NewNode new node\nfunc NewNode(scheme, addr string, ins *registry.ServiceInstance) Node {\n\tn := &DefaultNode{\n\t\tscheme: scheme,\n\t\taddr:   addr,\n\t}\n\tif ins != nil {\n\t\tn.name = ins.Name\n\t\tn.version = ins.Version\n\t\tn.metadata = ins.Metadata\n\t\tif str, ok := ins.Metadata[\"weight\"]; ok {\n\t\t\tif weight, err := strconv.ParseInt(str, 10, 64); err == nil {\n\t\t\t\tn.weight = &weight\n\t\t\t}\n\t\t}\n\t}\n\treturn n\n}\n"
  },
  {
    "path": "selector/default_selector.go",
    "content": "package selector\n\nimport (\n\t\"context\"\n\t\"sync/atomic\"\n)\n\nvar (\n\t_ Rebalancer = (*Default)(nil)\n\t_ Builder    = (*DefaultBuilder)(nil)\n)\n\n// Default is composite selector.\ntype Default struct {\n\tNodeBuilder WeightedNodeBuilder\n\tBalancer    Balancer\n\n\tnodes atomic.Value\n}\n\n// Select is select one node.\nfunc (d *Default) Select(ctx context.Context, opts ...SelectOption) (selected Node, done DoneFunc, err error) {\n\tvar (\n\t\toptions    SelectOptions\n\t\tcandidates []WeightedNode\n\t)\n\tnodes, ok := d.nodes.Load().([]WeightedNode)\n\tif !ok {\n\t\treturn nil, nil, ErrNoAvailable\n\t}\n\tfor _, o := range opts {\n\t\to(&options)\n\t}\n\tif len(options.NodeFilters) > 0 {\n\t\tnewNodes := make([]Node, len(nodes))\n\t\tfor i, wc := range nodes {\n\t\t\tnewNodes[i] = wc\n\t\t}\n\t\tfor _, filter := range options.NodeFilters {\n\t\t\tnewNodes = filter(ctx, newNodes)\n\t\t}\n\t\tcandidates = make([]WeightedNode, len(newNodes))\n\t\tfor i, n := range newNodes {\n\t\t\tcandidates[i] = n.(WeightedNode)\n\t\t}\n\t} else {\n\t\tcandidates = nodes\n\t}\n\n\tif len(candidates) == 0 {\n\t\treturn nil, nil, ErrNoAvailable\n\t}\n\twn, done, err := d.Balancer.Pick(ctx, candidates)\n\tif err != nil {\n\t\treturn nil, nil, err\n\t}\n\tp, ok := FromPeerContext(ctx)\n\tif ok {\n\t\tp.Node = wn.Raw()\n\t}\n\treturn wn.Raw(), done, nil\n}\n\n// Apply update nodes info.\nfunc (d *Default) Apply(nodes []Node) {\n\tweightedNodes := make([]WeightedNode, 0, len(nodes))\n\tfor _, n := range nodes {\n\t\tweightedNodes = append(weightedNodes, d.NodeBuilder.Build(n))\n\t}\n\t// TODO: Do not delete unchanged nodes\n\td.nodes.Store(weightedNodes)\n}\n\n// DefaultBuilder is de\ntype DefaultBuilder struct {\n\tNode     WeightedNodeBuilder\n\tBalancer BalancerBuilder\n}\n\n// Build create builder\nfunc (db *DefaultBuilder) Build() Selector {\n\treturn &Default{\n\t\tNodeBuilder: db.Node,\n\t\tBalancer:    db.Balancer.Build(),\n\t}\n}\n"
  },
  {
    "path": "selector/filter/version.go",
    "content": "package filter\n\nimport (\n\t\"context\"\n\n\t\"github.com/go-kratos/kratos/v2/selector\"\n)\n\n// Version is version filter.\nfunc Version(version string) selector.NodeFilter {\n\treturn func(_ context.Context, nodes []selector.Node) []selector.Node {\n\t\tnewNodes := make([]selector.Node, 0, len(nodes))\n\t\tfor _, n := range nodes {\n\t\t\tif n.Version() == version {\n\t\t\t\tnewNodes = append(newNodes, n)\n\t\t\t}\n\t\t}\n\t\treturn newNodes\n\t}\n}\n"
  },
  {
    "path": "selector/filter/version_test.go",
    "content": "package filter\n\nimport (\n\t\"context\"\n\t\"reflect\"\n\t\"testing\"\n\n\t\"github.com/go-kratos/kratos/v2/registry\"\n\t\"github.com/go-kratos/kratos/v2/selector\"\n)\n\nfunc TestVersion(t *testing.T) {\n\tf := Version(\"v2.0.0\")\n\tvar nodes []selector.Node\n\tnodes = append(nodes, selector.NewNode(\n\t\t\"http\",\n\t\t\"127.0.0.1:9090\",\n\t\t&registry.ServiceInstance{\n\t\t\tID:        \"127.0.0.1:9090\",\n\t\t\tName:      \"helloworld\",\n\t\t\tVersion:   \"v1.0.0\",\n\t\t\tEndpoints: []string{\"http://127.0.0.1:9090\"},\n\t\t}))\n\n\tnodes = append(nodes, selector.NewNode(\n\t\t\"http\",\n\t\t\"127.0.0.2:9090\",\n\t\t&registry.ServiceInstance{\n\t\t\tID:        \"127.0.0.2:9090\",\n\t\t\tName:      \"helloworld\",\n\t\t\tVersion:   \"v2.0.0\",\n\t\t\tEndpoints: []string{\"http://127.0.0.2:9090\"},\n\t\t}))\n\n\tnodes = f(context.Background(), nodes)\n\tif !reflect.DeepEqual(len(nodes), 1) {\n\t\tt.Errorf(\"expect %v, got %v\", 1, len(nodes))\n\t}\n\tif !reflect.DeepEqual(nodes[0].Address(), \"127.0.0.2:9090\") {\n\t\tt.Errorf(\"expect %v, got %v\", nodes[0].Address(), \"127.0.0.2:9090\")\n\t}\n}\n"
  },
  {
    "path": "selector/filter.go",
    "content": "package selector\n\nimport \"context\"\n\n// NodeFilter is select filter.\ntype NodeFilter func(context.Context, []Node) []Node\n"
  },
  {
    "path": "selector/global.go",
    "content": "package selector\n\nvar globalSelector = &wrapSelector{}\n\nvar _ Builder = (*wrapSelector)(nil)\n\n// wrapSelector wrapped Selector, help override global Selector implementation.\ntype wrapSelector struct{ Builder }\n\n// GlobalSelector returns global selector builder.\nfunc GlobalSelector() Builder {\n\tif globalSelector.Builder != nil {\n\t\treturn globalSelector\n\t}\n\treturn nil\n}\n\n// SetGlobalSelector set global selector builder.\nfunc SetGlobalSelector(builder Builder) {\n\tglobalSelector.Builder = builder\n}\n"
  },
  {
    "path": "selector/node/direct/direct.go",
    "content": "package direct\n\nimport (\n\t\"context\"\n\t\"sync/atomic\"\n\t\"time\"\n\n\t\"github.com/go-kratos/kratos/v2/selector\"\n)\n\nconst (\n\tdefaultWeight = 100\n)\n\nvar (\n\t_ selector.WeightedNode        = (*Node)(nil)\n\t_ selector.WeightedNodeBuilder = (*Builder)(nil)\n)\n\n// Node is endpoint instance\ntype Node struct {\n\tselector.Node\n\n\t// last lastPick timestamp\n\tlastPick atomic.Int64\n}\n\n// Builder is direct node builder\ntype Builder struct{}\n\n// Build create node\nfunc (*Builder) Build(n selector.Node) selector.WeightedNode {\n\treturn &Node{Node: n, lastPick: atomic.Int64{}}\n}\n\nfunc (n *Node) Pick() selector.DoneFunc {\n\tnow := time.Now().UnixNano()\n\tn.lastPick.Store(now)\n\treturn func(context.Context, selector.DoneInfo) {}\n}\n\n// Weight is node effective weight\nfunc (n *Node) Weight() float64 {\n\tif n.InitialWeight() != nil {\n\t\treturn float64(*n.InitialWeight())\n\t}\n\treturn defaultWeight\n}\n\nfunc (n *Node) PickElapsed() time.Duration {\n\treturn time.Duration(time.Now().UnixNano() - n.lastPick.Load())\n}\n\nfunc (n *Node) Raw() selector.Node {\n\treturn n.Node\n}\n"
  },
  {
    "path": "selector/node/direct/direct_test.go",
    "content": "package direct\n\nimport (\n\t\"context\"\n\t\"reflect\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/go-kratos/kratos/v2/registry\"\n\t\"github.com/go-kratos/kratos/v2/selector\"\n)\n\nfunc TestDirect(t *testing.T) {\n\tb := &Builder{}\n\twn := b.Build(selector.NewNode(\n\t\t\"http\",\n\t\t\"127.0.0.1:9090\",\n\t\t&registry.ServiceInstance{\n\t\t\tID:        \"127.0.0.1:9090\",\n\t\t\tName:      \"helloworld\",\n\t\t\tVersion:   \"v1.0.0\",\n\t\t\tEndpoints: []string{\"http://127.0.0.1:9090\"},\n\t\t\tMetadata:  map[string]string{\"weight\": \"10\"},\n\t\t}))\n\n\tdone := wn.Pick()\n\tif done == nil {\n\t\tt.Errorf(\"expect %v, got %v\", nil, done)\n\t}\n\ttime.Sleep(time.Millisecond * 10)\n\tdone(context.Background(), selector.DoneInfo{})\n\tif !reflect.DeepEqual(float64(10), wn.Weight()) {\n\t\tt.Errorf(\"expect %v, got %v\", float64(10), wn.Weight())\n\t}\n\tif time.Millisecond*20 <= wn.PickElapsed() {\n\t\tt.Errorf(\"20ms <= wn.PickElapsed()(%s)\", wn.PickElapsed())\n\t}\n\tif time.Millisecond*10 >= wn.PickElapsed() {\n\t\tt.Errorf(\"10ms >= wn.PickElapsed()(%s)\", wn.PickElapsed())\n\t}\n}\n\nfunc TestDirectDefaultWeight(t *testing.T) {\n\tb := &Builder{}\n\twn := b.Build(selector.NewNode(\n\t\t\"http\",\n\t\t\"127.0.0.1:9090\",\n\t\t&registry.ServiceInstance{\n\t\t\tID:        \"127.0.0.1:9090\",\n\t\t\tName:      \"helloworld\",\n\t\t\tVersion:   \"v1.0.0\",\n\t\t\tEndpoints: []string{\"http://127.0.0.1:9090\"},\n\t\t}))\n\n\tdone := wn.Pick()\n\tif done == nil {\n\t\tt.Errorf(\"expect %v, got %v\", nil, done)\n\t}\n\ttime.Sleep(time.Millisecond * 10)\n\tdone(context.Background(), selector.DoneInfo{})\n\tif !reflect.DeepEqual(float64(100), wn.Weight()) {\n\t\tt.Errorf(\"expect %v, got %v\", float64(100), wn.Weight())\n\t}\n\tif time.Millisecond*20 <= wn.PickElapsed() {\n\t\tt.Errorf(\"time.Millisecond*20 <= wn.PickElapsed()(%s)\", wn.PickElapsed())\n\t}\n\tif time.Millisecond*5 >= wn.PickElapsed() {\n\t\tt.Errorf(\"time.Millisecond*5 >= wn.PickElapsed()(%s)\", wn.PickElapsed())\n\t}\n}\n"
  },
  {
    "path": "selector/node/ewma/node.go",
    "content": "package ewma\n\nimport (\n\t\"context\"\n\t\"math\"\n\t\"net\"\n\t\"sync/atomic\"\n\t\"time\"\n\n\t\"github.com/go-kratos/kratos/v2/errors\"\n\t\"github.com/go-kratos/kratos/v2/selector\"\n)\n\nconst (\n\t// The mean lifetime of `cost`, it reaches its half-life after Tau*ln(2).\n\ttau = int64(time.Millisecond * 600)\n\t// if statistic not collected,we add a big lag penalty to endpoint\n\tpenalty = uint64(time.Microsecond * 100)\n)\n\nvar (\n\t_ selector.WeightedNode        = (*Node)(nil)\n\t_ selector.WeightedNodeBuilder = (*Builder)(nil)\n)\n\n// Node is endpoint instance\ntype Node struct {\n\tselector.Node\n\n\t// client statistic data\n\tlag       atomic.Int64\n\tsuccess   atomic.Uint64\n\tinflight  atomic.Int64\n\tinflights [200]atomic.Int64\n\t// last collected timestamp\n\tstamp atomic.Int64\n\t// request number in a period time\n\treqs atomic.Int64\n\t// last lastPick timestamp\n\tlastPick atomic.Int64\n\n\terrHandler   func(err error) (isErr bool)\n\tcachedWeight *atomic.Value\n}\n\ntype nodeWeight struct {\n\tvalue    float64\n\tupdateAt int64\n}\n\n// Builder is ewma node builder.\ntype Builder struct {\n\tErrHandler func(err error) (isErr bool)\n}\n\n// Build create a weighted node.\nfunc (b *Builder) Build(n selector.Node) selector.WeightedNode {\n\ts := &Node{\n\t\tNode:         n,\n\t\tinflights:    [200]atomic.Int64{},\n\t\terrHandler:   b.ErrHandler,\n\t\tcachedWeight: &atomic.Value{},\n\t}\n\ts.success.Store(1000)\n\ts.inflight.Store(1)\n\treturn s\n}\n\nfunc (n *Node) health() uint64 {\n\treturn n.success.Load()\n}\n\nfunc (n *Node) load() (load uint64) {\n\tnow := time.Now().UnixNano()\n\tavgLag := n.lag.Load()\n\tpredict := n.predict(avgLag, now)\n\n\tif avgLag == 0 {\n\t\t// penalty is the penalty value when there is no data when the node is just started.\n\t\tload = penalty * uint64(n.inflight.Load())\n\t\treturn\n\t}\n\tif predict > avgLag {\n\t\tavgLag = predict\n\t}\n\t// add 5ms to eliminate the latency gap between different zones\n\tavgLag += int64(time.Millisecond * 5)\n\tavgLag = int64(math.Sqrt(float64(avgLag)))\n\tload = uint64(avgLag) * uint64(n.inflight.Load())\n\treturn load\n}\n\nfunc (n *Node) predict(avgLag int64, now int64) (predict int64) {\n\tvar (\n\t\ttotal    int64\n\t\tslowNum  int\n\t\ttotalNum int\n\t)\n\tfor i := range n.inflights {\n\t\tstart := n.inflights[i].Load()\n\t\tif start != 0 {\n\t\t\ttotalNum++\n\t\t\tlag := now - start\n\t\t\tif lag > avgLag {\n\t\t\t\tslowNum++\n\t\t\t\ttotal += lag\n\t\t\t}\n\t\t}\n\t}\n\tif slowNum >= (totalNum/2 + 1) {\n\t\tpredict = total / int64(slowNum)\n\t}\n\treturn\n}\n\n// Pick pick a node.\nfunc (n *Node) Pick() selector.DoneFunc {\n\tstart := time.Now().UnixNano()\n\tn.lastPick.Store(start)\n\tn.inflight.Add(1)\n\treqs := n.reqs.Add(1)\n\tslot := reqs % 200\n\tswapped := n.inflights[slot].CompareAndSwap(0, start)\n\treturn func(_ context.Context, di selector.DoneInfo) {\n\t\tif swapped {\n\t\t\tn.inflights[slot].CompareAndSwap(start, 0)\n\t\t}\n\t\tn.inflight.Add(-1)\n\n\t\tnow := time.Now().UnixNano()\n\t\t// get moving average ratio w\n\t\tstamp := n.stamp.Swap(now)\n\t\ttd := now - stamp\n\t\tif td < 0 {\n\t\t\ttd = 0\n\t\t}\n\t\tw := math.Exp(float64(-td) / float64(tau))\n\n\t\tlag := now - start\n\t\tif lag < 0 {\n\t\t\tlag = 0\n\t\t}\n\t\toldLag := n.lag.Load()\n\t\tif oldLag == 0 {\n\t\t\tw = 0.0\n\t\t}\n\t\tlag = int64(float64(oldLag)*w + float64(lag)*(1.0-w))\n\t\tn.lag.Store(lag)\n\n\t\tsuccess := uint64(1000) // error value ,if error set 1\n\t\tif di.Err != nil {\n\t\t\tif n.errHandler != nil && n.errHandler(di.Err) {\n\t\t\t\tsuccess = 0\n\t\t\t}\n\t\t\tvar netErr net.Error\n\t\t\tif errors.Is(context.DeadlineExceeded, di.Err) || errors.Is(context.Canceled, di.Err) ||\n\t\t\t\terrors.IsServiceUnavailable(di.Err) || errors.IsGatewayTimeout(di.Err) || errors.As(di.Err, &netErr) {\n\t\t\t\tsuccess = 0\n\t\t\t}\n\t\t}\n\t\toldSuc := n.success.Load()\n\t\tsuccess = uint64(float64(oldSuc)*w + float64(success)*(1.0-w))\n\t\tn.success.Store(success)\n\t}\n}\n\n// Weight is node effective weight.\nfunc (n *Node) Weight() (weight float64) {\n\tw, ok := n.cachedWeight.Load().(*nodeWeight)\n\tnow := time.Now().UnixNano()\n\tif !ok || time.Duration(now-w.updateAt) > (time.Millisecond*5) {\n\t\thealth := n.health()\n\t\tload := n.load()\n\t\tweight = float64(health*uint64(time.Microsecond)*10) / float64(load)\n\t\tn.cachedWeight.Store(&nodeWeight{\n\t\t\tvalue:    weight,\n\t\t\tupdateAt: now,\n\t\t})\n\t} else {\n\t\tweight = w.value\n\t}\n\treturn\n}\n\nfunc (n *Node) PickElapsed() time.Duration {\n\treturn time.Duration(time.Now().UnixNano() - n.lastPick.Load())\n}\n\nfunc (n *Node) Raw() selector.Node {\n\treturn n.Node\n}\n"
  },
  {
    "path": "selector/node/ewma/node_test.go",
    "content": "package ewma\n\nimport (\n\t\"context\"\n\t\"net\"\n\t\"reflect\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/go-kratos/kratos/v2/registry\"\n\t\"github.com/go-kratos/kratos/v2/selector\"\n)\n\nfunc TestDirect(t *testing.T) {\n\tb := &Builder{}\n\twn := b.Build(selector.NewNode(\n\t\t\"http\",\n\t\t\"127.0.0.1:9090\",\n\t\t&registry.ServiceInstance{\n\t\t\tID:        \"127.0.0.1:9090\",\n\t\t\tName:      \"helloworld\",\n\t\t\tVersion:   \"v1.0.0\",\n\t\t\tEndpoints: []string{\"http://127.0.0.1:9090\"},\n\t\t\tMetadata:  map[string]string{\"weight\": \"10\"},\n\t\t}))\n\n\tif !reflect.DeepEqual(float64(100), wn.Weight()) {\n\t\tt.Errorf(\"expect %v, got %v\", 100, wn.Weight())\n\t}\n\tdone := wn.Pick()\n\tif done == nil {\n\t\tt.Errorf(\"done is equal to nil\")\n\t}\n\tdone2 := wn.Pick()\n\tif done2 == nil {\n\t\tt.Errorf(\"done2 is equal to nil\")\n\t}\n\n\ttime.Sleep(time.Millisecond * 15)\n\tdone(context.Background(), selector.DoneInfo{})\n\tif float64(70) >= wn.Weight() {\n\t\tt.Errorf(\"float64(30000) >= wn.Weight()(%v)\", wn.Weight())\n\t}\n\tif float64(1200) <= wn.Weight() {\n\t\tt.Errorf(\"float64(1000) <= wn.Weight()(%v)\", wn.Weight())\n\t}\n\tif time.Millisecond*30 <= wn.PickElapsed() {\n\t\tt.Errorf(\"time.Millisecond*30 <= wn.PickElapsed()(%v)\", wn.PickElapsed())\n\t}\n\tif time.Millisecond*5 >= wn.PickElapsed() {\n\t\tt.Errorf(\"time.Millisecond*5 >= wn.PickElapsed()(%v)\", wn.PickElapsed())\n\t}\n}\n\nfunc TestDirectError(t *testing.T) {\n\tb := &Builder{}\n\twn := b.Build(selector.NewNode(\n\t\t\"http\",\n\t\t\"127.0.0.1:9090\",\n\t\t&registry.ServiceInstance{\n\t\t\tID:        \"127.0.0.1:9090\",\n\t\t\tName:      \"helloworld\",\n\t\t\tVersion:   \"v1.0.0\",\n\t\t\tEndpoints: []string{\"http://127.0.0.1:9090\"},\n\t\t\tMetadata:  map[string]string{\"weight\": \"10\"},\n\t\t}))\n\n\tfor i := 0; i < 5; i++ {\n\t\tvar err error\n\t\tif i != 0 {\n\t\t\terr = context.DeadlineExceeded\n\t\t}\n\t\tdone := wn.Pick()\n\t\tif done == nil {\n\t\t\tt.Errorf(\"expect not nil, got nil\")\n\t\t}\n\t\ttime.Sleep(time.Millisecond * 20)\n\t\tdone(context.Background(), selector.DoneInfo{Err: err})\n\t}\n\tif float64(1000) >= wn.Weight() {\n\t\tt.Errorf(\"float64(1000) >= wn.Weight()(%v)\", wn.Weight())\n\t}\n\tif float64(2000) <= wn.Weight() {\n\t\tt.Errorf(\"float64(2000) <= wn.Weight()(%v)\", wn.Weight())\n\t}\n}\n\nfunc TestDirectErrorHandler(t *testing.T) {\n\tb := &Builder{\n\t\tErrHandler: func(err error) bool {\n\t\t\treturn err != nil\n\t\t},\n\t}\n\twn := b.Build(selector.NewNode(\n\t\t\"http\",\n\t\t\"127.0.0.1:9090\",\n\t\t&registry.ServiceInstance{\n\t\t\tID:        \"127.0.0.1:9090\",\n\t\t\tName:      \"helloworld\",\n\t\t\tVersion:   \"v1.0.0\",\n\t\t\tEndpoints: []string{\"http://127.0.0.1:9090\"},\n\t\t\tMetadata:  map[string]string{\"weight\": \"10\"},\n\t\t}))\n\terrs := []error{\n\t\tcontext.DeadlineExceeded,\n\t\tcontext.Canceled,\n\t\tnet.ErrClosed,\n\t}\n\tfor i := 0; i < 5; i++ {\n\t\tvar err error\n\t\tif i != 0 {\n\t\t\terr = errs[i%len(errs)]\n\t\t}\n\t\tdone := wn.Pick()\n\t\tif done == nil {\n\t\t\tt.Errorf(\"expect not nil, got nil\")\n\t\t}\n\t\ttime.Sleep(time.Millisecond * 20)\n\t\tdone(context.Background(), selector.DoneInfo{Err: err})\n\t}\n\tif float64(1000) >= wn.Weight() {\n\t\tt.Errorf(\"float64(100) >= wn.Weight()(%v)\", wn.Weight())\n\t}\n\tif float64(2000) <= wn.Weight() {\n\t\tt.Errorf(\"float64(200) <= wn.Weight()(%v)\", wn.Weight())\n\t}\n}\n\nfunc BenchmarkPickAndWeight(b *testing.B) {\n\tbu := &Builder{}\n\tnode := bu.Build(selector.NewNode(\n\t\t\"http\",\n\t\t\"127.0.0.1:9090\",\n\t\t&registry.ServiceInstance{\n\t\t\tID:        \"127.0.0.1:9090\",\n\t\t\tName:      \"helloworld\",\n\t\t\tVersion:   \"v1.0.0\",\n\t\t\tEndpoints: []string{\"http://127.0.0.1:9090\"},\n\t\t\tMetadata:  map[string]string{\"weight\": \"10\"},\n\t\t}))\n\tdi := selector.DoneInfo{}\n\tb.RunParallel(func(pb *testing.PB) {\n\t\tfor pb.Next() {\n\t\t\tdone := node.Pick()\n\t\t\tnode.Weight()\n\t\t\tdone(context.Background(), di)\n\t\t}\n\t})\n}\n"
  },
  {
    "path": "selector/options.go",
    "content": "package selector\n\n// SelectOptions is Select Options.\ntype SelectOptions struct {\n\tNodeFilters []NodeFilter\n}\n\n// SelectOption is Selector option.\ntype SelectOption func(*SelectOptions)\n\n// WithNodeFilter with filter options\nfunc WithNodeFilter(fn ...NodeFilter) SelectOption {\n\treturn func(opts *SelectOptions) {\n\t\topts.NodeFilters = fn\n\t}\n}\n"
  },
  {
    "path": "selector/p2c/p2c.go",
    "content": "package p2c\n\nimport (\n\t\"context\"\n\t\"math/rand/v2\"\n\t\"sync\"\n\t\"sync/atomic\"\n\t\"time\"\n\n\t\"github.com/go-kratos/kratos/v2/selector\"\n\t\"github.com/go-kratos/kratos/v2/selector/node/ewma\"\n)\n\nconst (\n\tforcePick = time.Second * 3\n\t// Name is p2c(Pick of 2 choices) balancer name\n\tName = \"p2c\"\n)\n\nvar _ selector.Balancer = (*Balancer)(nil)\n\n// Option is p2c builder option.\ntype Option func(o *options)\n\n// options is p2c builder options\ntype options struct{}\n\n// New creates a p2c selector.\nfunc New(opts ...Option) selector.Selector {\n\treturn NewBuilder(opts...).Build()\n}\n\n// Balancer is p2c selector.\ntype Balancer struct {\n\tmu     sync.Mutex\n\tr      *rand.Rand\n\tpicked atomic.Bool\n}\n\n// choose two distinct nodes.\nfunc (s *Balancer) prePick(nodes []selector.WeightedNode) (nodeA selector.WeightedNode, nodeB selector.WeightedNode) {\n\ts.mu.Lock()\n\ta := s.r.IntN(len(nodes))\n\tb := s.r.IntN(len(nodes) - 1)\n\ts.mu.Unlock()\n\tif b >= a {\n\t\tb = b + 1\n\t}\n\tnodeA, nodeB = nodes[a], nodes[b]\n\treturn\n}\n\n// Pick pick a node.\nfunc (s *Balancer) Pick(_ context.Context, nodes []selector.WeightedNode) (selector.WeightedNode, selector.DoneFunc, error) {\n\tif len(nodes) == 0 {\n\t\treturn nil, nil, selector.ErrNoAvailable\n\t}\n\tif len(nodes) == 1 {\n\t\tdone := nodes[0].Pick()\n\t\treturn nodes[0], done, nil\n\t}\n\n\tvar pc, upc selector.WeightedNode\n\tnodeA, nodeB := s.prePick(nodes)\n\t// meta.Weight is the weight set by the service publisher in discovery\n\tif nodeB.Weight() > nodeA.Weight() {\n\t\tpc, upc = nodeB, nodeA\n\t} else {\n\t\tpc, upc = nodeA, nodeB\n\t}\n\n\t// If the failed node has never been selected once during forceGap, it is forced to be selected once\n\t// Take advantage of forced opportunities to trigger updates of success rate and delay\n\tif upc.PickElapsed() > forcePick && s.picked.CompareAndSwap(false, true) {\n\t\tdefer s.picked.Store(false)\n\t\tpc = upc\n\t}\n\tdone := pc.Pick()\n\treturn pc, done, nil\n}\n\n// NewBuilder returns a selector builder with p2c balancer\nfunc NewBuilder(opts ...Option) selector.Builder {\n\tvar option options\n\tfor _, opt := range opts {\n\t\topt(&option)\n\t}\n\treturn &selector.DefaultBuilder{\n\t\tBalancer: &Builder{},\n\t\tNode:     &ewma.Builder{},\n\t}\n}\n\n// Builder is p2c builder\ntype Builder struct{}\n\n// Build creates Balancer\nfunc (b *Builder) Build() selector.Balancer {\n\treturn &Balancer{r: rand.New(rand.NewPCG(uint64(time.Now().UnixNano()), 0))}\n}\n"
  },
  {
    "path": "selector/p2c/p2c_test.go",
    "content": "package p2c\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"math/rand/v2\"\n\t\"reflect\"\n\t\"sync\"\n\t\"sync/atomic\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/go-kratos/kratos/v2/registry\"\n\t\"github.com/go-kratos/kratos/v2/selector\"\n\t\"github.com/go-kratos/kratos/v2/selector/filter\"\n)\n\nfunc TestWrr3(t *testing.T) {\n\tp2c := New()\n\tvar nodes []selector.Node\n\tfor i := 0; i < 3; i++ {\n\t\taddr := fmt.Sprintf(\"127.0.0.%d:8080\", i)\n\t\tnodes = append(nodes, selector.NewNode(\n\t\t\t\"http\",\n\t\t\taddr,\n\t\t\t&registry.ServiceInstance{\n\t\t\t\tID:       addr,\n\t\t\t\tVersion:  \"v2.0.0\",\n\t\t\t\tMetadata: map[string]string{\"weight\": \"10\"},\n\t\t\t}))\n\t}\n\tp2c.Apply(nodes)\n\tvar count1, count2, count3 int64\n\tgroup := &sync.WaitGroup{}\n\tvar lk sync.Mutex\n\tfor i := 0; i < 9000; i++ {\n\t\tgroup.Add(1)\n\t\tgo func() {\n\t\t\tdefer group.Done()\n\t\t\tlk.Lock()\n\t\t\td := time.Duration(rand.IntN(500)) * time.Millisecond\n\t\t\tlk.Unlock()\n\t\t\ttime.Sleep(d)\n\t\t\tn, done, err := p2c.Select(context.Background(), selector.WithNodeFilter(filter.Version(\"v2.0.0\")))\n\t\t\tif err != nil {\n\t\t\t\tt.Errorf(\"expect %v, got %v\", nil, err)\n\t\t\t}\n\t\t\tif n == nil {\n\t\t\t\tt.Errorf(\"expect %v, got %v\", nil, n)\n\t\t\t}\n\t\t\tif done == nil {\n\t\t\t\tt.Errorf(\"expect %v, got %v\", nil, done)\n\t\t\t}\n\t\t\ttime.Sleep(time.Millisecond * 10)\n\t\t\tdone(context.Background(), selector.DoneInfo{})\n\t\t\tif n.Address() == \"127.0.0.0:8080\" {\n\t\t\t\tatomic.AddInt64(&count1, 1)\n\t\t\t} else if n.Address() == \"127.0.0.1:8080\" {\n\t\t\t\tatomic.AddInt64(&count2, 1)\n\t\t\t} else if n.Address() == \"127.0.0.2:8080\" {\n\t\t\t\tatomic.AddInt64(&count3, 1)\n\t\t\t}\n\t\t}()\n\t}\n\tgroup.Wait()\n\tif count1 <= int64(1500) {\n\t\tt.Errorf(\"count1(%v) <= int64(1500)\", count1)\n\t}\n\tif count1 >= int64(4500) {\n\t\tt.Errorf(\"count1(%v) >= int64(4500),\", count1)\n\t}\n\tif count2 <= int64(1500) {\n\t\tt.Errorf(\"count2(%v) <= int64(1500)\", count2)\n\t}\n\tif count2 >= int64(4500) {\n\t\tt.Errorf(\"count2(%v) >= int64(4500),\", count2)\n\t}\n\tif count3 <= int64(1500) {\n\t\tt.Errorf(\"count3(%v) <= int64(1500)\", count3)\n\t}\n\tif count3 >= int64(4500) {\n\t\tt.Errorf(\"count3(%v) >= int64(4500),\", count3)\n\t}\n}\n\nfunc TestEmpty(t *testing.T) {\n\tb := &Balancer{}\n\t_, _, err := b.Pick(context.Background(), []selector.WeightedNode{})\n\tif err == nil {\n\t\tt.Errorf(\"expect %v, got %v\", nil, err)\n\t}\n}\n\nfunc TestOne(t *testing.T) {\n\tp2c := New()\n\tvar nodes []selector.Node\n\tfor i := 0; i < 1; i++ {\n\t\taddr := fmt.Sprintf(\"127.0.0.%d:8080\", i)\n\t\tnodes = append(nodes, selector.NewNode(\n\t\t\t\"http\",\n\t\t\taddr,\n\t\t\t&registry.ServiceInstance{\n\t\t\t\tID:       addr,\n\t\t\t\tVersion:  \"v2.0.0\",\n\t\t\t\tMetadata: map[string]string{\"weight\": \"10\"},\n\t\t\t}))\n\t}\n\tp2c.Apply(nodes)\n\tn, done, err := p2c.Select(context.Background(), selector.WithNodeFilter(filter.Version(\"v2.0.0\")))\n\tif err != nil {\n\t\tt.Errorf(\"expect %v, got %v\", nil, err)\n\t}\n\tif n == nil {\n\t\tt.Errorf(\"expect %v, got %v\", nil, n)\n\t}\n\tif done == nil {\n\t\tt.Errorf(\"expect %v, got %v\", nil, done)\n\t}\n\tif !reflect.DeepEqual(\"127.0.0.0:8080\", n.Address()) {\n\t\tt.Errorf(\"expect %v, got %v\", \"127.0.0.0:8080\", n.Address())\n\t}\n}\n"
  },
  {
    "path": "selector/peer.go",
    "content": "package selector\n\nimport (\n\t\"context\"\n)\n\ntype peerKey struct{}\n\n// Peer contains the information of the peer for an RPC, such as the address\n// and authentication information.\ntype Peer struct {\n\t// node is the peer node.\n\tNode Node\n}\n\n// NewPeerContext creates a new context with peer information attached.\nfunc NewPeerContext(ctx context.Context, p *Peer) context.Context {\n\treturn context.WithValue(ctx, peerKey{}, p)\n}\n\n// FromPeerContext returns the peer information in ctx if it exists.\nfunc FromPeerContext(ctx context.Context) (p *Peer, ok bool) {\n\tp, ok = ctx.Value(peerKey{}).(*Peer)\n\treturn\n}\n"
  },
  {
    "path": "selector/peer_test.go",
    "content": "package selector\n\nimport (\n\t\"context\"\n\t\"testing\"\n)\n\nfunc TestPeer(t *testing.T) {\n\tp := Peer{\n\t\tNode: mockWeightedNode{},\n\t}\n\tctx := NewPeerContext(context.Background(), &p)\n\tp2, ok := FromPeerContext(ctx)\n\tif !ok || p2.Node == nil {\n\t\tt.Fatalf(\" no peer found!\")\n\t}\n}\n\nfunc TestNotPeer(t *testing.T) {\n\t_, ok := FromPeerContext(context.Background())\n\tif ok {\n\t\tt.Fatalf(\"test no peer found peer!\")\n\t}\n}\n"
  },
  {
    "path": "selector/random/random.go",
    "content": "package random\n\nimport (\n\t\"context\"\n\t\"math/rand/v2\"\n\n\t\"github.com/go-kratos/kratos/v2/selector\"\n\t\"github.com/go-kratos/kratos/v2/selector/node/direct\"\n)\n\nconst (\n\t// Name is random balancer name\n\tName = \"random\"\n)\n\nvar _ selector.Balancer = (*Balancer)(nil)\n\n// Option is random builder option.\ntype Option func(o *options)\n\n// options is random builder options\ntype options struct{}\n\n// Balancer is a random balancer.\ntype Balancer struct{}\n\n// New a random selector.\nfunc New(opts ...Option) selector.Selector {\n\treturn NewBuilder(opts...).Build()\n}\n\n// Pick is pick a weighted node.\nfunc (p *Balancer) Pick(_ context.Context, nodes []selector.WeightedNode) (selector.WeightedNode, selector.DoneFunc, error) {\n\tif len(nodes) == 0 {\n\t\treturn nil, nil, selector.ErrNoAvailable\n\t}\n\tcur := rand.IntN(len(nodes))\n\tselected := nodes[cur]\n\td := selected.Pick()\n\treturn selected, d, nil\n}\n\n// NewBuilder returns a selector builder with random balancer\nfunc NewBuilder(opts ...Option) selector.Builder {\n\tvar option options\n\tfor _, opt := range opts {\n\t\topt(&option)\n\t}\n\treturn &selector.DefaultBuilder{\n\t\tBalancer: &Builder{},\n\t\tNode:     &direct.Builder{},\n\t}\n}\n\n// Builder is random builder\ntype Builder struct{}\n\n// Build creates Balancer\nfunc (b *Builder) Build() selector.Balancer {\n\treturn &Balancer{}\n}\n"
  },
  {
    "path": "selector/random/random_test.go",
    "content": "package random\n\nimport (\n\t\"context\"\n\t\"testing\"\n\n\t\"github.com/go-kratos/kratos/v2/registry\"\n\t\"github.com/go-kratos/kratos/v2/selector\"\n\t\"github.com/go-kratos/kratos/v2/selector/filter\"\n)\n\nfunc TestWrr(t *testing.T) {\n\trandom := New()\n\tvar nodes []selector.Node\n\tnodes = append(nodes, selector.NewNode(\n\t\t\"http\",\n\t\t\"127.0.0.1:8080\",\n\t\t&registry.ServiceInstance{\n\t\t\tID:       \"127.0.0.1:8080\",\n\t\t\tVersion:  \"v2.0.0\",\n\t\t\tMetadata: map[string]string{\"weight\": \"10\"},\n\t\t}))\n\tnodes = append(nodes, selector.NewNode(\n\t\t\"http\",\n\t\t\"127.0.0.1:9090\",\n\t\t&registry.ServiceInstance{\n\t\t\tID:       \"127.0.0.1:9090\",\n\t\t\tVersion:  \"v2.0.0\",\n\t\t\tMetadata: map[string]string{\"weight\": \"20\"},\n\t\t}))\n\trandom.Apply(nodes)\n\tvar count1, count2 int\n\tfor i := 0; i < 1000; i++ {\n\t\tn, done, err := random.Select(context.Background(), selector.WithNodeFilter(filter.Version(\"v2.0.0\")))\n\t\tif err != nil {\n\t\t\tt.Errorf(\"expect no error, got %v\", err)\n\t\t}\n\t\tif done == nil {\n\t\t\tt.Errorf(\"expect not nil, got:%v\", done)\n\t\t}\n\t\tif n == nil {\n\t\t\tt.Errorf(\"expect not nil, got:%v\", n)\n\t\t}\n\t\tdone(context.Background(), selector.DoneInfo{})\n\t\tif n.Address() == \"127.0.0.1:8080\" {\n\t\t\tcount1++\n\t\t} else if n.Address() == \"127.0.0.1:9090\" {\n\t\t\tcount2++\n\t\t}\n\t}\n\tif count1 <= 400 {\n\t\tt.Errorf(\"count1(%v) <= 400\", count1)\n\t}\n\tif count1 >= 600 {\n\t\tt.Errorf(\"count1(%v) >= 600\", count1)\n\t}\n\tif count2 <= 400 {\n\t\tt.Errorf(\"count2(%v) <= 400\", count2)\n\t}\n\tif count2 >= 600 {\n\t\tt.Errorf(\"count2(%v) >= 600\", count2)\n\t}\n}\n\nfunc TestEmpty(t *testing.T) {\n\tb := &Balancer{}\n\t_, _, err := b.Pick(context.Background(), []selector.WeightedNode{})\n\tif err == nil {\n\t\tt.Errorf(\"expect nil, got %v\", err)\n\t}\n}\n"
  },
  {
    "path": "selector/selector.go",
    "content": "package selector\n\nimport (\n\t\"context\"\n\n\t\"github.com/go-kratos/kratos/v2/errors\"\n)\n\n// ErrNoAvailable is no available node.\nvar ErrNoAvailable = errors.ServiceUnavailable(\"no_available_node\", \"\")\n\n// Selector is node pick balancer.\ntype Selector interface {\n\tRebalancer\n\n\t// Select nodes\n\t// if err == nil, selected and done must not be empty.\n\tSelect(ctx context.Context, opts ...SelectOption) (selected Node, done DoneFunc, err error)\n}\n\n// Rebalancer is nodes rebalancer.\ntype Rebalancer interface {\n\t// Apply is apply all nodes when any changes happen\n\tApply(nodes []Node)\n}\n\n// Builder build selector\ntype Builder interface {\n\tBuild() Selector\n}\n\n// Node is node interface.\ntype Node interface {\n\t// Scheme is service node scheme\n\tScheme() string\n\n\t// Address is the unique address under the same service\n\tAddress() string\n\n\t// ServiceName is service name\n\tServiceName() string\n\n\t// InitialWeight is the initial value of scheduling weight\n\t// if not set return nil\n\tInitialWeight() *int64\n\n\t// Version is service node version\n\tVersion() string\n\n\t// Metadata is the kv pair metadata associated with the service instance.\n\t// version,namespace,region,protocol etc..\n\tMetadata() map[string]string\n}\n\n// DoneInfo is callback info when RPC invoke done.\ntype DoneInfo struct {\n\t// Response Error\n\tErr error\n\t// Response Metadata\n\tReplyMD ReplyMD\n\n\t// BytesSent indicates if any bytes have been sent to the server.\n\tBytesSent bool\n\t// BytesReceived indicates if any byte has been received from the server.\n\tBytesReceived bool\n}\n\n// ReplyMD is Reply Metadata.\ntype ReplyMD interface {\n\tGet(key string) string\n}\n\n// DoneFunc is callback function when RPC invoke done.\ntype DoneFunc func(ctx context.Context, di DoneInfo)\n"
  },
  {
    "path": "selector/selector_test.go",
    "content": "package selector\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"math/rand/v2\"\n\t\"reflect\"\n\t\"sync/atomic\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/go-kratos/kratos/v2/registry\"\n)\n\nvar errNodeNotMatch = errors.New(\"node is not match\")\n\ntype mockWeightedNode struct {\n\tNode\n\n\tlastPick int64\n}\n\n// Raw returns the original node\nfunc (n *mockWeightedNode) Raw() Node {\n\treturn n.Node\n}\n\n// Weight is the runtime calculated weight\nfunc (n *mockWeightedNode) Weight() float64 {\n\tif n.InitialWeight() != nil {\n\t\treturn float64(*n.InitialWeight())\n\t}\n\treturn 100\n}\n\n// Pick the node\nfunc (n *mockWeightedNode) Pick() DoneFunc {\n\tnow := time.Now().UnixNano()\n\tatomic.StoreInt64(&n.lastPick, now)\n\treturn func(context.Context, DoneInfo) {}\n}\n\n// PickElapsed is time elapsed since the latest pick\nfunc (n *mockWeightedNode) PickElapsed() time.Duration {\n\treturn time.Duration(time.Now().UnixNano() - atomic.LoadInt64(&n.lastPick))\n}\n\ntype mockWeightedNodeBuilder struct{}\n\nfunc (b *mockWeightedNodeBuilder) Build(n Node) WeightedNode {\n\treturn &mockWeightedNode{Node: n}\n}\n\nfunc mockFilter(version string) NodeFilter {\n\treturn func(_ context.Context, nodes []Node) []Node {\n\t\tnewNodes := nodes[:0]\n\t\tfor _, n := range nodes {\n\t\t\tif n.Version() == version {\n\t\t\t\tnewNodes = append(newNodes, n)\n\t\t\t}\n\t\t}\n\t\treturn newNodes\n\t}\n}\n\ntype mockBalancerBuilder struct{}\n\nfunc (b *mockBalancerBuilder) Build() Balancer {\n\treturn &mockBalancer{}\n}\n\ntype mockBalancer struct{}\n\nfunc (b *mockBalancer) Pick(_ context.Context, nodes []WeightedNode) (selected WeightedNode, done DoneFunc, err error) {\n\tif len(nodes) == 0 {\n\t\terr = ErrNoAvailable\n\t\treturn\n\t}\n\tcur := rand.IntN(len(nodes))\n\tselected = nodes[cur]\n\tdone = selected.Pick()\n\treturn\n}\n\ntype mockMustErrorBalancerBuilder struct{}\n\nfunc (b *mockMustErrorBalancerBuilder) Build() Balancer {\n\treturn &mockMustErrorBalancer{}\n}\n\ntype mockMustErrorBalancer struct{}\n\nfunc (b *mockMustErrorBalancer) Pick(_ context.Context, _ []WeightedNode) (selected WeightedNode, done DoneFunc, err error) {\n\treturn nil, nil, errNodeNotMatch\n}\n\nfunc TestDefault(t *testing.T) {\n\tbuilder := DefaultBuilder{\n\t\tNode:     &mockWeightedNodeBuilder{},\n\t\tBalancer: &mockBalancerBuilder{},\n\t}\n\tselector := builder.Build()\n\tvar nodes []Node\n\tnodes = append(nodes, NewNode(\n\t\t\"http\",\n\t\t\"127.0.0.1:8080\",\n\t\t&registry.ServiceInstance{\n\t\t\tID:        \"127.0.0.1:8080\",\n\t\t\tName:      \"helloworld\",\n\t\t\tVersion:   \"v2.0.0\",\n\t\t\tEndpoints: []string{\"http://127.0.0.1:8080\"},\n\t\t\tMetadata:  map[string]string{\"weight\": \"10\"},\n\t\t}))\n\tnodes = append(nodes, NewNode(\n\t\t\"http\",\n\t\t\"127.0.0.1:9090\",\n\t\t&registry.ServiceInstance{\n\t\t\tID:        \"127.0.0.1:9090\",\n\t\t\tName:      \"helloworld\",\n\t\t\tVersion:   \"v1.0.0\",\n\t\t\tEndpoints: []string{\"http://127.0.0.1:9090\"},\n\t\t\tMetadata:  map[string]string{\"weight\": \"10\"},\n\t\t}))\n\n\tselector.Apply(nodes)\n\tn, done, err := selector.Select(context.Background(), WithNodeFilter(mockFilter(\"v2.0.0\")))\n\tif err != nil {\n\t\tt.Errorf(\"expect %v, got %v\", nil, err)\n\t}\n\tif n == nil {\n\t\tt.Errorf(\"expect %v, got %v\", nil, n)\n\t}\n\tif done == nil {\n\t\tt.Errorf(\"expect %v, got %v\", nil, done)\n\t}\n\tif !reflect.DeepEqual(\"v2.0.0\", n.Version()) {\n\t\tt.Errorf(\"expect %v, got %v\", \"v2.0.0\", n.Version())\n\t}\n\tif n.Scheme() == \"\" {\n\t\tt.Errorf(\"expect %v, got %v\", \"\", n.Scheme())\n\t}\n\tif n.Address() == \"\" {\n\t\tt.Errorf(\"expect %v, got %v\", \"\", n.Address())\n\t}\n\tif !reflect.DeepEqual(int64(10), *n.InitialWeight()) {\n\t\tt.Errorf(\"expect %v, got %v\", 10, *n.InitialWeight())\n\t}\n\tif n.Metadata() == nil {\n\t\tt.Errorf(\"expect %v, got %v\", nil, n.Metadata())\n\t}\n\tif !reflect.DeepEqual(\"helloworld\", n.ServiceName()) {\n\t\tt.Errorf(\"expect %v, got %v\", \"helloworld\", n.ServiceName())\n\t}\n\tdone(context.Background(), DoneInfo{})\n\n\t// peer in ctx\n\tctx := NewPeerContext(context.Background(), &Peer{\n\t\tNode: mockWeightedNode{},\n\t})\n\tn, done, err = selector.Select(ctx)\n\tif err != nil {\n\t\tt.Errorf(\"expect %v, got %v\", ErrNoAvailable, err)\n\t}\n\tif done == nil {\n\t\tt.Errorf(\"expect %v, got %v\", nil, done)\n\t}\n\tif n == nil {\n\t\tt.Errorf(\"expect %v, got %v\", nil, n)\n\t}\n\n\t// no v3.0.0 instance\n\tn, done, err = selector.Select(context.Background(), WithNodeFilter(mockFilter(\"v3.0.0\")))\n\tif !errors.Is(ErrNoAvailable, err) {\n\t\tt.Errorf(\"expect %v, got %v\", ErrNoAvailable, err)\n\t}\n\tif done != nil {\n\t\tt.Errorf(\"expect %v, got %v\", nil, done)\n\t}\n\tif n != nil {\n\t\tt.Errorf(\"expect %v, got %v\", nil, n)\n\t}\n\n\t// apply zero instance\n\tselector.Apply([]Node{})\n\tn, done, err = selector.Select(context.Background(), WithNodeFilter(mockFilter(\"v2.0.0\")))\n\tif !errors.Is(ErrNoAvailable, err) {\n\t\tt.Errorf(\"expect %v, got %v\", ErrNoAvailable, err)\n\t}\n\tif done != nil {\n\t\tt.Errorf(\"expect %v, got %v\", nil, done)\n\t}\n\tif n != nil {\n\t\tt.Errorf(\"expect %v, got %v\", nil, n)\n\t}\n\n\t// apply zero instance\n\tselector.Apply(nil)\n\tn, done, err = selector.Select(context.Background(), WithNodeFilter(mockFilter(\"v2.0.0\")))\n\tif !errors.Is(ErrNoAvailable, err) {\n\t\tt.Errorf(\"expect %v, got %v\", ErrNoAvailable, err)\n\t}\n\tif done != nil {\n\t\tt.Errorf(\"expect %v, got %v\", nil, done)\n\t}\n\tif n != nil {\n\t\tt.Errorf(\"expect %v, got %v\", nil, n)\n\t}\n\n\t// without node_filters\n\tn, done, err = selector.Select(context.Background())\n\tif !errors.Is(ErrNoAvailable, err) {\n\t\tt.Errorf(\"expect %v, got %v\", ErrNoAvailable, err)\n\t}\n\tif done != nil {\n\t\tt.Errorf(\"expect %v, got %v\", nil, done)\n\t}\n\tif n != nil {\n\t\tt.Errorf(\"expect %v, got %v\", nil, n)\n\t}\n}\n\nfunc TestWithoutApply(t *testing.T) {\n\tbuilder := DefaultBuilder{\n\t\tNode:     &mockWeightedNodeBuilder{},\n\t\tBalancer: &mockBalancerBuilder{},\n\t}\n\tselector := builder.Build()\n\tn, done, err := selector.Select(context.Background())\n\tif !errors.Is(ErrNoAvailable, err) {\n\t\tt.Errorf(\"expect %v, got %v\", ErrNoAvailable, err)\n\t}\n\tif done != nil {\n\t\tt.Errorf(\"expect %v, got %v\", nil, done)\n\t}\n\tif n != nil {\n\t\tt.Errorf(\"expect %v, got %v\", nil, n)\n\t}\n}\n\nfunc TestNoPick(t *testing.T) {\n\tbuilder := DefaultBuilder{\n\t\tNode:     &mockWeightedNodeBuilder{},\n\t\tBalancer: &mockMustErrorBalancerBuilder{},\n\t}\n\tvar nodes []Node\n\tnodes = append(nodes, NewNode(\n\t\t\"http\",\n\t\t\"127.0.0.1:8080\",\n\t\t&registry.ServiceInstance{\n\t\t\tID:        \"127.0.0.1:8080\",\n\t\t\tName:      \"helloworld\",\n\t\t\tVersion:   \"v2.0.0\",\n\t\t\tEndpoints: []string{\"http://127.0.0.1:8080\"},\n\t\t\tMetadata:  map[string]string{\"weight\": \"10\"},\n\t\t}))\n\tnodes = append(nodes, NewNode(\n\t\t\"http\",\n\t\t\"127.0.0.1:9090\",\n\t\t&registry.ServiceInstance{\n\t\t\tID:        \"127.0.0.1:9090\",\n\t\t\tName:      \"helloworld\",\n\t\t\tVersion:   \"v1.0.0\",\n\t\t\tEndpoints: []string{\"http://127.0.0.1:9090\"},\n\t\t\tMetadata:  map[string]string{\"weight\": \"10\"},\n\t\t}))\n\tselector := builder.Build()\n\tselector.Apply(nodes)\n\tn, done, err := selector.Select(context.Background())\n\tif !errors.Is(errNodeNotMatch, err) {\n\t\tt.Errorf(\"expect %v, got %v\", errNodeNotMatch, err)\n\t}\n\tif done != nil {\n\t\tt.Errorf(\"expect %v, got %v\", nil, done)\n\t}\n\tif n != nil {\n\t\tt.Errorf(\"expect %v, got %v\", nil, n)\n\t}\n}\n\nfunc TestGlobalSelector(t *testing.T) {\n\tbuilder := DefaultBuilder{\n\t\tNode:     &mockWeightedNodeBuilder{},\n\t\tBalancer: &mockBalancerBuilder{},\n\t}\n\tSetGlobalSelector(&builder)\n\n\tgBuilder := GlobalSelector()\n\tif gBuilder == nil {\n\t\tt.Errorf(\"expect %v, got %v\", nil, gBuilder)\n\t}\n}\n"
  },
  {
    "path": "selector/wrr/wrr.go",
    "content": "package wrr\n\nimport (\n\t\"context\"\n\t\"sync\"\n\n\t\"github.com/go-kratos/kratos/v2/selector\"\n\t\"github.com/go-kratos/kratos/v2/selector/node/direct\"\n)\n\nconst (\n\t// Name is wrr(Weighted Round Robin) balancer name\n\tName = \"wrr\"\n)\n\nvar _ selector.Balancer = (*Balancer)(nil)\n\n// Option is wrr builder option.\ntype Option func(o *options)\n\n// options is wrr builder options\ntype options struct{}\n\n// Balancer is a wrr balancer.\ntype Balancer struct {\n\tmu            sync.Mutex\n\tcurrentWeight map[string]float64\n\tlastNodes     []selector.WeightedNode\n}\n\n// equalNodes checks if two slices of WeightedNode contain the same nodes\nfunc equalNodes(a, b []selector.WeightedNode) bool {\n\tif len(a) != len(b) {\n\t\treturn false\n\t}\n\n\t// Create a map of addresses from slice a\n\taMap := make(map[string]bool, len(a))\n\tfor _, node := range a {\n\t\taMap[node.Address()] = true\n\t}\n\n\t// Check if all nodes in slice b exist in slice a\n\tfor _, node := range b {\n\t\tif !aMap[node.Address()] {\n\t\t\treturn false\n\t\t}\n\t}\n\n\treturn true\n}\n\n// New random a selector.\nfunc New(opts ...Option) selector.Selector {\n\treturn NewBuilder(opts...).Build()\n}\n\n// Pick is pick a weighted node.\nfunc (p *Balancer) Pick(_ context.Context, nodes []selector.WeightedNode) (selector.WeightedNode, selector.DoneFunc, error) {\n\tif len(nodes) == 0 {\n\t\treturn nil, nil, selector.ErrNoAvailable\n\t}\n\n\tp.mu.Lock()\n\tdefer p.mu.Unlock()\n\n\t// Check if the node list has changed\n\tif !equalNodes(p.lastNodes, nodes) {\n\t\t// Update lastNodes\n\t\tp.lastNodes = make([]selector.WeightedNode, len(nodes))\n\t\tcopy(p.lastNodes, nodes)\n\n\t\t// Create a set of current node addresses for cleanup\n\t\tcurrentNodes := make(map[string]bool, len(nodes))\n\t\tfor _, node := range nodes {\n\t\t\tcurrentNodes[node.Address()] = true\n\t\t}\n\n\t\t// Clean up stale entries from currentWeight map\n\t\tfor address := range p.currentWeight {\n\t\t\tif !currentNodes[address] {\n\t\t\t\tdelete(p.currentWeight, address)\n\t\t\t}\n\t\t}\n\t}\n\n\tvar totalWeight float64\n\tvar selected selector.WeightedNode\n\tvar selectWeight float64\n\n\t// nginx wrr load balancing algorithm: http://blog.csdn.net/zhangskd/article/details/50194069\n\tfor _, node := range nodes {\n\t\ttotalWeight += node.Weight()\n\t\tcwt := p.currentWeight[node.Address()]\n\t\t// current += effectiveWeight\n\t\tcwt += node.Weight()\n\t\tp.currentWeight[node.Address()] = cwt\n\t\tif selected == nil || selectWeight < cwt {\n\t\t\tselectWeight = cwt\n\t\t\tselected = node\n\t\t}\n\t}\n\tp.currentWeight[selected.Address()] = selectWeight - totalWeight\n\n\td := selected.Pick()\n\treturn selected, d, nil\n}\n\n// NewBuilder returns a selector builder with wrr balancer\nfunc NewBuilder(opts ...Option) selector.Builder {\n\tvar option options\n\tfor _, opt := range opts {\n\t\topt(&option)\n\t}\n\treturn &selector.DefaultBuilder{\n\t\tBalancer: &Builder{},\n\t\tNode:     &direct.Builder{},\n\t}\n}\n\n// Builder is wrr builder\ntype Builder struct{}\n\n// Build creates Balancer\nfunc (b *Builder) Build() selector.Balancer {\n\treturn &Balancer{currentWeight: make(map[string]float64)}\n}\n"
  },
  {
    "path": "selector/wrr/wrr_test.go",
    "content": "package wrr\n\nimport (\n\t\"context\"\n\t\"reflect\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/go-kratos/kratos/v2/registry\"\n\t\"github.com/go-kratos/kratos/v2/selector\"\n\t\"github.com/go-kratos/kratos/v2/selector/filter\"\n)\n\nfunc TestWrr(t *testing.T) {\n\twrr := New()\n\tvar nodes []selector.Node\n\tnodes = append(nodes, selector.NewNode(\n\t\t\"http\",\n\t\t\"127.0.0.1:8080\",\n\t\t&registry.ServiceInstance{\n\t\t\tID:       \"127.0.0.1:8080\",\n\t\t\tVersion:  \"v2.0.0\",\n\t\t\tMetadata: map[string]string{\"weight\": \"10\"},\n\t\t}))\n\tnodes = append(nodes, selector.NewNode(\n\t\t\"http\",\n\t\t\"127.0.0.1:9090\",\n\t\t&registry.ServiceInstance{\n\t\t\tID:       \"127.0.0.1:9090\",\n\t\t\tVersion:  \"v2.0.0\",\n\t\t\tMetadata: map[string]string{\"weight\": \"20\"},\n\t\t}))\n\twrr.Apply(nodes)\n\tvar count1, count2 int\n\tfor i := 0; i < 90; i++ {\n\t\tn, done, err := wrr.Select(context.Background(), selector.WithNodeFilter(filter.Version(\"v2.0.0\")))\n\t\tif err != nil {\n\t\t\tt.Errorf(\"expect no error, got %v\", err)\n\t\t}\n\t\tif done == nil {\n\t\t\tt.Errorf(\"expect done callback, got nil\")\n\t\t}\n\t\tif n == nil {\n\t\t\tt.Errorf(\"expect node, got nil\")\n\t\t}\n\t\tdone(context.Background(), selector.DoneInfo{})\n\t\tif n.Address() == \"127.0.0.1:8080\" {\n\t\t\tcount1++\n\t\t} else if n.Address() == \"127.0.0.1:9090\" {\n\t\t\tcount2++\n\t\t}\n\t}\n\tif !reflect.DeepEqual(count1, 30) {\n\t\tt.Errorf(\"expect 30, got %d\", count1)\n\t}\n\tif !reflect.DeepEqual(count2, 60) {\n\t\tt.Errorf(\"expect 60, got %d\", count2)\n\t}\n}\n\n// TestCurrentWeightCleanup tests that stale entries in currentWeight map are cleaned up\nfunc TestCurrentWeightCleanup(t *testing.T) {\n\tbalancer := &Balancer{currentWeight: make(map[string]float64)}\n\n\t// Create initial nodes\n\tnodes1 := []selector.WeightedNode{\n\t\t&mockWeightedNode{address: \"node1\", weight: 10},\n\t\t&mockWeightedNode{address: \"node2\", weight: 20},\n\t\t&mockWeightedNode{address: \"node3\", weight: 30},\n\t}\n\n\t// Pick from initial nodes to populate currentWeight\n\tfor i := 0; i < 10; i++ {\n\t\t_, _, err := balancer.Pick(context.Background(), nodes1)\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"unexpected error: %v\", err)\n\t\t}\n\t}\n\n\t// Verify all 3 nodes are in currentWeight map\n\tif len(balancer.currentWeight) != 3 {\n\t\tt.Errorf(\"expected 3 entries in currentWeight, got %d\", len(balancer.currentWeight))\n\t}\n\n\t// Change to different set of nodes (simulating service discovery update)\n\tnodes2 := []selector.WeightedNode{\n\t\t&mockWeightedNode{address: \"node2\", weight: 20}, // only node2 remains\n\t\t&mockWeightedNode{address: \"node4\", weight: 40}, // node4 is new\n\t}\n\n\t// Pick from new nodes\n\t_, _, err := balancer.Pick(context.Background(), nodes2)\n\tif err != nil {\n\t\tt.Fatalf(\"unexpected error: %v\", err)\n\t}\n\n\t// Verify that stale entries (node1, node3) are cleaned up\n\tif len(balancer.currentWeight) != 2 {\n\t\tt.Errorf(\"expected 2 entries in currentWeight after cleanup, got %d\", len(balancer.currentWeight))\n\t}\n\n\t// Verify that node2 and node4 are present, but node1 and node3 are not\n\tif _, exists := balancer.currentWeight[\"node1\"]; exists {\n\t\tt.Error(\"stale entry node1 should have been cleaned up\")\n\t}\n\tif _, exists := balancer.currentWeight[\"node3\"]; exists {\n\t\tt.Error(\"stale entry node3 should have been cleaned up\")\n\t}\n\tif _, exists := balancer.currentWeight[\"node2\"]; !exists {\n\t\tt.Error(\"node2 should be present in currentWeight\")\n\t}\n\tif _, exists := balancer.currentWeight[\"node4\"]; !exists {\n\t\tt.Error(\"node4 should be present in currentWeight\")\n\t}\n}\n\n// TestCleanupOnlyWhenNodesChange verifies that cleanup logic only runs when nodes actually change\nfunc TestCleanupOnlyWhenNodesChange(t *testing.T) {\n\t// Create a custom balancer that tracks cleanup calls\n\ttype trackingBalancer struct {\n\t\t*Balancer\n\t\tcleanupCount int\n\t}\n\n\t// Override the Pick method to count cleanup operations\n\tbalancer := &trackingBalancer{\n\t\tBalancer: &Balancer{currentWeight: make(map[string]float64)},\n\t}\n\n\toriginalPick := func(_ context.Context, nodes []selector.WeightedNode) (selector.WeightedNode, selector.DoneFunc, error) {\n\t\tif len(nodes) == 0 {\n\t\t\treturn nil, nil, selector.ErrNoAvailable\n\t\t}\n\n\t\tbalancer.mu.Lock()\n\t\tdefer balancer.mu.Unlock()\n\n\t\t// Check if the node list has changed\n\t\tif len(balancer.lastNodes) != len(nodes) || !equalNodes(balancer.lastNodes, nodes) {\n\t\t\tbalancer.cleanupCount++ // Count cleanup operations\n\n\t\t\t// Update lastNodes\n\t\t\tbalancer.lastNodes = make([]selector.WeightedNode, len(nodes))\n\t\t\tcopy(balancer.lastNodes, nodes)\n\n\t\t\t// Create a set of current node addresses for cleanup\n\t\t\tcurrentNodes := make(map[string]bool)\n\t\t\tfor _, node := range nodes {\n\t\t\t\tcurrentNodes[node.Address()] = true\n\t\t\t}\n\n\t\t\t// Clean up stale entries from currentWeight map\n\t\t\tfor address := range balancer.currentWeight {\n\t\t\t\tif !currentNodes[address] {\n\t\t\t\t\tdelete(balancer.currentWeight, address)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tvar totalWeight float64\n\t\tvar selected selector.WeightedNode\n\t\tvar selectWeight float64\n\n\t\t// nginx wrr load balancing algorithm\n\t\tfor _, node := range nodes {\n\t\t\ttotalWeight += node.Weight()\n\t\t\tcwt := balancer.currentWeight[node.Address()]\n\t\t\tcwt += node.Weight()\n\t\t\tbalancer.currentWeight[node.Address()] = cwt\n\t\t\tif selected == nil || selectWeight < cwt {\n\t\t\t\tselectWeight = cwt\n\t\t\t\tselected = node\n\t\t\t}\n\t\t}\n\t\tbalancer.currentWeight[selected.Address()] = selectWeight - totalWeight\n\n\t\td := selected.Pick()\n\t\treturn selected, d, nil\n\t}\n\n\tctx := context.Background()\n\tnodes1 := []selector.WeightedNode{\n\t\t&mockWeightedNode{address: \"node1\", weight: 10},\n\t\t&mockWeightedNode{address: \"node2\", weight: 20},\n\t}\n\n\tnodes2 := []selector.WeightedNode{\n\t\t&mockWeightedNode{address: \"node3\", weight: 30},\n\t\t&mockWeightedNode{address: \"node4\", weight: 40},\n\t}\n\n\tvar err error\n\n\t// First call with nodes1 - should trigger cleanup (initialization)\n\t_, _, err = originalPick(ctx, nodes1)\n\tif err != nil {\n\t\tt.Fatalf(\"unexpected error: %v\", err)\n\t}\n\n\tif balancer.cleanupCount != 1 {\n\t\tt.Errorf(\"expected 1 cleanup call after first pick, got %d\", balancer.cleanupCount)\n\t}\n\n\t// Multiple calls with same nodes1 - should NOT trigger additional cleanup\n\tfor i := 0; i < 5; i++ {\n\t\t_, _, err = originalPick(ctx, nodes1)\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"unexpected error: %v\", err)\n\t\t}\n\t}\n\n\tif balancer.cleanupCount != 1 {\n\t\tt.Errorf(\"expected still 1 cleanup call after repeated picks with same nodes, got %d\", balancer.cleanupCount)\n\t}\n\n\t// Call with different nodes2 - should trigger cleanup\n\t_, _, err = originalPick(ctx, nodes2)\n\tif err != nil {\n\t\tt.Fatalf(\"unexpected error: %v\", err)\n\t}\n\n\tif balancer.cleanupCount != 2 {\n\t\tt.Errorf(\"expected 2 cleanup calls after node change, got %d\", balancer.cleanupCount)\n\t}\n\n\t// Multiple calls with same nodes2 - should NOT trigger additional cleanup\n\tfor i := 0; i < 3; i++ {\n\t\t_, _, err = originalPick(ctx, nodes2)\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"unexpected error: %v\", err)\n\t\t}\n\t}\n\n\tif balancer.cleanupCount != 2 {\n\t\tt.Errorf(\"expected still 2 cleanup calls after repeated picks with same nodes, got %d\", balancer.cleanupCount)\n\t}\n}\n\n// mockWeightedNode is a mock implementation for testing\ntype mockWeightedNode struct {\n\taddress string\n\tweight  float64\n}\n\nfunc (m *mockWeightedNode) Raw() selector.Node { return nil }\nfunc (m *mockWeightedNode) Weight() float64    { return m.weight }\nfunc (m *mockWeightedNode) Address() string    { return m.address }\nfunc (m *mockWeightedNode) Pick() selector.DoneFunc {\n\treturn func(context.Context, selector.DoneInfo) {}\n}\nfunc (m *mockWeightedNode) PickElapsed() time.Duration  { return 0 }\nfunc (m *mockWeightedNode) Scheme() string              { return \"http\" }\nfunc (m *mockWeightedNode) ServiceName() string         { return \"test\" }\nfunc (m *mockWeightedNode) InitialWeight() *int64       { return nil }\nfunc (m *mockWeightedNode) Version() string             { return \"v1.0.0\" }\nfunc (m *mockWeightedNode) Metadata() map[string]string { return nil }\n\nfunc TestEmpty(t *testing.T) {\n\tb := &Balancer{}\n\t_, _, err := b.Pick(context.Background(), []selector.WeightedNode{})\n\tif err == nil {\n\t\tt.Errorf(\"expect no error, got %v\", err)\n\t}\n}\n\n// BenchmarkPickWithSameNodes benchmarks Pick() calls with the same node set\n// This demonstrates the performance improvement where cleanup only happens on node changes\nfunc BenchmarkPickWithSameNodes(b *testing.B) {\n\tbalancer := &Balancer{currentWeight: make(map[string]float64)}\n\n\t// Create a fixed set of nodes\n\tnodes := []selector.WeightedNode{\n\t\t&mockWeightedNode{address: \"node1\", weight: 10},\n\t\t&mockWeightedNode{address: \"node2\", weight: 20},\n\t\t&mockWeightedNode{address: \"node3\", weight: 30},\n\t\t&mockWeightedNode{address: \"node4\", weight: 40},\n\t\t&mockWeightedNode{address: \"node5\", weight: 50},\n\t}\n\n\tctx := context.Background()\n\n\tb.ResetTimer()\n\tb.ReportAllocs()\n\n\t// Benchmark Pick() calls with the same nodes\n\t// After the first call, no cleanup should occur on subsequent calls\n\tfor i := 0; i < b.N; i++ {\n\t\t_, _, err := balancer.Pick(ctx, nodes)\n\t\tif err != nil {\n\t\t\tb.Fatalf(\"unexpected error: %v\", err)\n\t\t}\n\t}\n}\n\n// BenchmarkPickWithChangingNodes benchmarks Pick() calls with changing node sets\n// This shows the overhead when nodes actually change (expected to be slower)\nfunc BenchmarkPickWithChangingNodes(b *testing.B) {\n\tbalancer := &Balancer{currentWeight: make(map[string]float64)}\n\n\t// Create alternating sets of nodes to simulate node changes\n\tnodes1 := []selector.WeightedNode{\n\t\t&mockWeightedNode{address: \"node1\", weight: 10},\n\t\t&mockWeightedNode{address: \"node2\", weight: 20},\n\t}\n\n\tnodes2 := []selector.WeightedNode{\n\t\t&mockWeightedNode{address: \"node3\", weight: 30},\n\t\t&mockWeightedNode{address: \"node4\", weight: 40},\n\t}\n\n\tctx := context.Background()\n\n\tb.ResetTimer()\n\tb.ReportAllocs()\n\n\t// Benchmark Pick() calls with alternating node sets\n\t// This will trigger cleanup on every call\n\tfor i := 0; i < b.N; i++ {\n\t\tvar nodes []selector.WeightedNode\n\t\tif i%2 == 0 {\n\t\t\tnodes = nodes1\n\t\t} else {\n\t\t\tnodes = nodes2\n\t\t}\n\n\t\t_, _, err := balancer.Pick(ctx, nodes)\n\t\tif err != nil {\n\t\t\tb.Fatalf(\"unexpected error: %v\", err)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "third_party/README.md",
    "content": "# third_party\n"
  },
  {
    "path": "third_party/buf/validate/README.md",
    "content": "# protovalidate\n\n* https://github.com/bufbuild/protovalidate"
  },
  {
    "path": "third_party/buf/validate/validate.proto",
    "content": "// Copyright 2023 Buf Technologies, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nsyntax = \"proto2\";\n\npackage buf.validate;\n\nimport \"google/protobuf/descriptor.proto\";\nimport \"google/protobuf/duration.proto\";\nimport \"google/protobuf/timestamp.proto\";\n\noption go_package = \"buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go/buf/validate\";\noption java_multiple_files = true;\noption java_outer_classname = \"ValidateProto\";\noption java_package = \"build.buf.validate\";\n\n// MessageOptions is an extension to google.protobuf.MessageOptions. It allows\n// the addition of validation rules at the message level. These rules can be\n// applied to incoming messages to ensure they meet certain criteria before\n// being processed.\nextend google.protobuf.MessageOptions {\n  // Rules specify the validations to be performed on this message. By default,\n  // no validation is performed against a message.\n  optional MessageConstraints message = 1159;\n}\n\n// OneofOptions is an extension to google.protobuf.OneofOptions. It allows\n// the addition of validation rules on a oneof. These rules can be\n// applied to incoming messages to ensure they meet certain criteria before\n// being processed.\nextend google.protobuf.OneofOptions {\n  // Rules specify the validations to be performed on this oneof. By default,\n  // no validation is performed against a oneof.\n  optional OneofConstraints oneof = 1159;\n}\n\n// FieldOptions is an extension to google.protobuf.FieldOptions. It allows\n// the addition of validation rules at the field level. These rules can be\n// applied to incoming messages to ensure they meet certain criteria before\n// being processed.\nextend google.protobuf.FieldOptions {\n  // Rules specify the validations to be performed on this field. By default,\n  // no validation is performed against a field.\n  optional FieldConstraints field = 1159;\n\n  // Specifies predefined rules. When extending a standard constraint message,\n  // this adds additional CEL expressions that apply when the extension is used.\n  //\n  // ```proto\n  // extend buf.validate.Int32Rules {\n  //   bool is_zero [(buf.validate.predefined).cel = {\n  //     id: \"int32.is_zero\",\n  //     message: \"value must be zero\",\n  //     expression: \"!rule || this == 0\",\n  //   }];\n  // }\n  //\n  // message Foo {\n  //   int32 reserved = 1 [(buf.validate.field).int32.(is_zero) = true];\n  // }\n  // ```\n  optional PredefinedConstraints predefined = 1160;\n}\n\n// `Constraint` represents a validation rule written in the Common Expression\n// Language (CEL) syntax. Each Constraint includes a unique identifier, an\n// optional error message, and the CEL expression to evaluate. For more\n// information on CEL, [see our documentation](https://github.com/bufbuild/protovalidate/blob/main/docs/cel.md).\n//\n// ```proto\n// message Foo {\n//   option (buf.validate.message).cel = {\n//     id: \"foo.bar\"\n//     message: \"bar must be greater than 0\"\n//     expression: \"this.bar > 0\"\n//   };\n//   int32 bar = 1;\n// }\n// ```\nmessage Constraint {\n  // `id` is a string that serves as a machine-readable name for this Constraint.\n  // It should be unique within its scope, which could be either a message or a field.\n  optional string id = 1;\n\n  // `message` is an optional field that provides a human-readable error message\n  // for this Constraint when the CEL expression evaluates to false. If a\n  // non-empty message is provided, any strings resulting from the CEL\n  // expression evaluation are ignored.\n  optional string message = 2;\n\n  // `expression` is the actual CEL expression that will be evaluated for\n  // validation. This string must resolve to either a boolean or a string\n  // value. If the expression evaluates to false or a non-empty string, the\n  // validation is considered failed, and the message is rejected.\n  optional string expression = 3;\n}\n\n// MessageConstraints represents validation rules that are applied to the entire message.\n// It includes disabling options and a list of Constraint messages representing Common Expression Language (CEL) validation rules.\nmessage MessageConstraints {\n  // `disabled` is a boolean flag that, when set to true, nullifies any validation rules for this message.\n  // This includes any fields within the message that would otherwise support validation.\n  //\n  // ```proto\n  // message MyMessage {\n  //   // validation will be bypassed for this message\n  //   option (buf.validate.message).disabled = true;\n  // }\n  // ```\n  optional bool disabled = 1;\n\n  // `cel` is a repeated field of type Constraint. Each Constraint specifies a validation rule to be applied to this message.\n  // These constraints are written in Common Expression Language (CEL) syntax. For more information on\n  // CEL, [see our documentation](https://github.com/bufbuild/protovalidate/blob/main/docs/cel.md).\n  //\n  //\n  // ```proto\n  // message MyMessage {\n  //   // The field `foo` must be greater than 42.\n  //   option (buf.validate.message).cel = {\n  //     id: \"my_message.value\",\n  //     message: \"value must be greater than 42\",\n  //     expression: \"this.foo > 42\",\n  //   };\n  //   optional int32 foo = 1;\n  // }\n  // ```\n  repeated Constraint cel = 3;\n}\n\n// The `OneofConstraints` message type enables you to manage constraints for\n// oneof fields in your protobuf messages.\nmessage OneofConstraints {\n  // If `required` is true, exactly one field of the oneof must be present. A\n  // validation error is returned if no fields in the oneof are present. The\n  // field itself may still be a default value; further constraints\n  // should be placed on the fields themselves to ensure they are valid values,\n  // such as `min_len` or `gt`.\n  //\n  // ```proto\n  // message MyMessage {\n  //   oneof value {\n  //     // Either `a` or `b` must be set. If `a` is set, it must also be\n  //     // non-empty; whereas if `b` is set, it can still be an empty string.\n  //     option (buf.validate.oneof).required = true;\n  //     string a = 1 [(buf.validate.field).string.min_len = 1];\n  //     string b = 2;\n  //   }\n  // }\n  // ```\n  optional bool required = 1;\n}\n\n// FieldConstraints encapsulates the rules for each type of field. Depending on\n// the field, the correct set should be used to ensure proper validations.\nmessage FieldConstraints {\n  // `cel` is a repeated field used to represent a textual expression\n  // in the Common Expression Language (CEL) syntax. For more information on\n  // CEL, [see our documentation](https://github.com/bufbuild/protovalidate/blob/main/docs/cel.md).\n  //\n  // ```proto\n  // message MyMessage {\n  //   // The field `value` must be greater than 42.\n  //   optional int32 value = 1 [(buf.validate.field).cel = {\n  //     id: \"my_message.value\",\n  //     message: \"value must be greater than 42\",\n  //     expression: \"this > 42\",\n  //   }];\n  // }\n  // ```\n  repeated Constraint cel = 23;\n  // If `required` is true, the field must be populated. A populated field can be\n  // described as \"serialized in the wire format,\" which includes:\n  //\n  // - the following \"nullable\" fields must be explicitly set to be considered populated:\n  //   - singular message fields (whose fields may be unpopulated/default values)\n  //   - member fields of a oneof (may be their default value)\n  //   - proto3 optional fields (may be their default value)\n  //   - proto2 scalar fields (both optional and required)\n  // - proto3 scalar fields must be non-zero to be considered populated\n  // - repeated and map fields must be non-empty to be considered populated\n  //\n  // ```proto\n  // message MyMessage {\n  //   // The field `value` must be set to a non-null value.\n  //   optional MyOtherMessage value = 1 [(buf.validate.field).required = true];\n  // }\n  // ```\n  optional bool required = 25;\n  // Skip validation on the field if its value matches the specified criteria.\n  // See Ignore enum for details.\n  //\n  // ```proto\n  // message UpdateRequest {\n  //   // The uri rule only applies if the field is populated and not an empty\n  //   // string.\n  //   optional string url = 1 [\n  //     (buf.validate.field).ignore = IGNORE_IF_DEFAULT_VALUE,\n  //     (buf.validate.field).string.uri = true,\n  //   ];\n  // }\n  // ```\n  optional Ignore ignore = 27;\n\n  oneof type {\n    // Scalar Field Types\n    FloatRules float = 1;\n    DoubleRules double = 2;\n    Int32Rules int32 = 3;\n    Int64Rules int64 = 4;\n    UInt32Rules uint32 = 5;\n    UInt64Rules uint64 = 6;\n    SInt32Rules sint32 = 7;\n    SInt64Rules sint64 = 8;\n    Fixed32Rules fixed32 = 9;\n    Fixed64Rules fixed64 = 10;\n    SFixed32Rules sfixed32 = 11;\n    SFixed64Rules sfixed64 = 12;\n    BoolRules bool = 13;\n    StringRules string = 14;\n    BytesRules bytes = 15;\n\n    // Complex Field Types\n    EnumRules enum = 16;\n    RepeatedRules repeated = 18;\n    MapRules map = 19;\n\n    // Well-Known Field Types\n    AnyRules any = 20;\n    DurationRules duration = 21;\n    TimestampRules timestamp = 22;\n  }\n\n  // DEPRECATED: use ignore=IGNORE_ALWAYS instead. TODO: remove this field pre-v1.\n  optional bool skipped = 24 [deprecated = true];\n  // DEPRECATED: use ignore=IGNORE_IF_UNPOPULATED instead. TODO: remove this field pre-v1.\n  optional bool ignore_empty = 26 [deprecated = true];\n}\n\n// PredefinedConstraints are custom constraints that can be re-used with\n// multiple fields.\nmessage PredefinedConstraints {\n  // `cel` is a repeated field used to represent a textual expression\n  // in the Common Expression Language (CEL) syntax. For more information on\n  // CEL, [see our documentation](https://github.com/bufbuild/protovalidate/blob/main/docs/cel.md).\n  //\n  // ```proto\n  // message MyMessage {\n  //   // The field `value` must be greater than 42.\n  //   optional int32 value = 1 [(buf.validate.predefined).cel = {\n  //     id: \"my_message.value\",\n  //     message: \"value must be greater than 42\",\n  //     expression: \"this > 42\",\n  //   }];\n  // }\n  // ```\n  repeated Constraint cel = 1;\n}\n\n// Specifies how FieldConstraints.ignore behaves. See the documentation for\n// FieldConstraints.required for definitions of \"populated\" and \"nullable\".\nenum Ignore {\n  // buf:lint:ignore ENUM_NO_ALLOW_ALIAS // allowance for deprecations. TODO: remove pre-v1.\n  option allow_alias = true;\n  // Validation is only skipped if it's an unpopulated nullable fields.\n  //\n  // ```proto\n  // syntax=\"proto3\";\n  //\n  // message Request {\n  //   // The uri rule applies to any value, including the empty string.\n  //   string foo = 1 [\n  //     (buf.validate.field).string.uri = true\n  //   ];\n  //\n  //   // The uri rule only applies if the field is set, including if it's\n  //   // set to the empty string.\n  //   optional string bar = 2 [\n  //     (buf.validate.field).string.uri = true\n  //   ];\n  //\n  //   // The min_items rule always applies, even if the list is empty.\n  //   repeated string baz = 3 [\n  //     (buf.validate.field).repeated.min_items = 3\n  //   ];\n  //\n  //   // The custom CEL rule applies only if the field is set, including if\n  //   // it's the \"zero\" value of that message.\n  //   SomeMessage quux = 4 [\n  //     (buf.validate.field).cel = {/* ... */}\n  //   ];\n  // }\n  // ```\n  IGNORE_UNSPECIFIED = 0;\n\n  // Validation is skipped if the field is unpopulated. This rule is redundant\n  // if the field is already nullable. This value is equivalent behavior to the\n  // deprecated ignore_empty rule.\n  //\n  // ```proto\n  // syntax=\"proto3\n  //\n  // message Request {\n  //   // The uri rule applies only if the value is not the empty string.\n  //   string foo = 1 [\n  //     (buf.validate.field).string.uri = true,\n  //     (buf.validate.field).ignore = IGNORE_IF_UNPOPULATED\n  //   ];\n  //\n  //   // IGNORE_IF_UNPOPULATED is equivalent to IGNORE_UNSPECIFIED in this\n  //   // case: the uri rule only applies if the field is set, including if\n  //   // it's set to the empty string.\n  //   optional string bar = 2 [\n  //     (buf.validate.field).string.uri = true,\n  //     (buf.validate.field).ignore = IGNORE_IF_UNPOPULATED\n  //   ];\n  //\n  //   // The min_items rule only applies if the list has at least one item.\n  //   repeated string baz = 3 [\n  //     (buf.validate.field).repeated.min_items = 3,\n  //     (buf.validate.field).ignore = IGNORE_IF_UNPOPULATED\n  //   ];\n  //\n  //   // IGNORE_IF_UNPOPULATED is equivalent to IGNORE_UNSPECIFIED in this\n  //   // case: the custom CEL rule applies only if the field is set, including\n  //   // if it's the \"zero\" value of that message.\n  //   SomeMessage quux = 4 [\n  //     (buf.validate.field).cel = {/* ... */},\n  //     (buf.validate.field).ignore = IGNORE_IF_UNPOPULATED\n  //   ];\n  // }\n  // ```\n  IGNORE_IF_UNPOPULATED = 1;\n\n  // Validation is skipped if the field is unpopulated or if it is a nullable\n  // field populated with its default value. This is typically the zero or\n  // empty value, but proto2 scalars support custom defaults. For messages, the\n  // default is a non-null message with all its fields unpopulated.\n  //\n  // ```proto\n  // syntax=\"proto3\n  //\n  // message Request {\n  //   // IGNORE_IF_DEFAULT_VALUE is equivalent to IGNORE_IF_UNPOPULATED in\n  //   // this case; the uri rule applies only if the value is not the empty\n  //   // string.\n  //   string foo = 1 [\n  //     (buf.validate.field).string.uri = true,\n  //     (buf.validate.field).ignore = IGNORE_IF_DEFAULT_VALUE\n  //   ];\n  //\n  //   // The uri rule only applies if the field is set to a value other than\n  //   // the empty string.\n  //   optional string bar = 2 [\n  //     (buf.validate.field).string.uri = true,\n  //     (buf.validate.field).ignore = IGNORE_IF_DEFAULT_VALUE\n  //   ];\n  //\n  //   // IGNORE_IF_DEFAULT_VALUE is equivalent to IGNORE_IF_UNPOPULATED in\n  //   // this case; the min_items rule only applies if the list has at least\n  //   // one item.\n  //   repeated string baz = 3 [\n  //     (buf.validate.field).repeated.min_items = 3,\n  //     (buf.validate.field).ignore = IGNORE_IF_DEFAULT_VALUE\n  //   ];\n  //\n  //   // The custom CEL rule only applies if the field is set to a value other\n  //   // than an empty message (i.e., fields are unpopulated).\n  //   SomeMessage quux = 4 [\n  //     (buf.validate.field).cel = {/* ... */},\n  //     (buf.validate.field).ignore = IGNORE_IF_DEFAULT_VALUE\n  //   ];\n  // }\n  // ```\n  //\n  // This rule is affected by proto2 custom default values:\n  //\n  // ```proto\n  // syntax=\"proto2\";\n  //\n  // message Request {\n  //   // The gt rule only applies if the field is set and it's value is not\n  //   the default (i.e., not -42). The rule even applies if the field is set\n  //   to zero since the default value differs.\n  //   optional int32 value = 1 [\n  //     default = -42,\n  //     (buf.validate.field).int32.gt = 0,\n  //     (buf.validate.field).ignore = IGNORE_IF_DEFAULT_VALUE\n  //   ];\n  // }\n  IGNORE_IF_DEFAULT_VALUE = 2;\n\n  // The validation rules of this field will be skipped and not evaluated. This\n  // is useful for situations that necessitate turning off the rules of a field\n  // containing a message that may not make sense in the current context, or to\n  // temporarily disable constraints during development.\n  //\n  // ```proto\n  // message MyMessage {\n  //   // The field's rules will always be ignored, including any validation's\n  //   // on value's fields.\n  //   MyOtherMessage value = 1 [\n  //     (buf.validate.field).ignore = IGNORE_ALWAYS];\n  // }\n  // ```\n  IGNORE_ALWAYS = 3;\n\n  // Deprecated: Use IGNORE_IF_UNPOPULATED instead. TODO: Remove this value pre-v1.\n  IGNORE_EMPTY = 1 [deprecated = true];\n  // Deprecated: Use IGNORE_IF_DEFAULT_VALUE. TODO: Remove this value pre-v1.\n  IGNORE_DEFAULT = 2 [deprecated = true];\n}\n\n// FloatRules describes the constraints applied to `float` values. These\n// rules may also be applied to the `google.protobuf.FloatValue` Well-Known-Type.\nmessage FloatRules {\n  // `const` requires the field value to exactly match the specified value. If\n  // the field value doesn't match, an error message is generated.\n  //\n  // ```proto\n  // message MyFloat {\n  //   // value must equal 42.0\n  //   float value = 1 [(buf.validate.field).float.const = 42.0];\n  // }\n  // ```\n  optional float const = 1 [(predefined).cel = {\n    id: \"float.const\"\n    expression: \"this != rules.const ? 'value must equal %s'.format([rules.const]) : ''\"\n  }];\n\n  oneof less_than {\n    // `lt` requires the field value to be less than the specified value (field <\n    // value). If the field value is equal to or greater than the specified value,\n    // an error message is generated.\n    //\n    // ```proto\n    // message MyFloat {\n    //   // value must be less than 10.0\n    //   float value = 1 [(buf.validate.field).float.lt = 10.0];\n    // }\n    // ```\n    float lt = 2 [(predefined).cel = {\n      id: \"float.lt\"\n      expression:\n          \"!has(rules.gte) && !has(rules.gt) && (this.isNan() || this >= rules.lt)\"\n              \"? 'value must be less than %s'.format([rules.lt]) : ''\"\n    }];\n\n    // `lte` requires the field value to be less than or equal to the specified\n    // value (field <= value). If the field value is greater than the specified\n    // value, an error message is generated.\n    //\n    // ```proto\n    // message MyFloat {\n    //   // value must be less than or equal to 10.0\n    //   float value = 1 [(buf.validate.field).float.lte = 10.0];\n    // }\n    // ```\n    float lte = 3 [(predefined).cel = {\n      id: \"float.lte\"\n      expression:\n          \"!has(rules.gte) && !has(rules.gt) && (this.isNan() || this > rules.lte)\"\n              \"? 'value must be less than or equal to %s'.format([rules.lte]) : ''\"\n    }];\n  }\n\n  oneof greater_than {\n    // `gt` requires the field value to be greater than the specified value\n    // (exclusive). If the value of `gt` is larger than a specified `lt` or\n    // `lte`, the range is reversed, and the field value must be outside the\n    // specified range. If the field value doesn't meet the required conditions,\n    // an error message is generated.\n    //\n    // ```proto\n    // message MyFloat {\n    //   // value must be greater than 5.0 [float.gt]\n    //   float value = 1 [(buf.validate.field).float.gt = 5.0];\n    //\n    //   // value must be greater than 5 and less than 10.0 [float.gt_lt]\n    //   float other_value = 2 [(buf.validate.field).float = { gt: 5.0, lt: 10.0 }];\n    //\n    //   // value must be greater than 10 or less than 5.0 [float.gt_lt_exclusive]\n    //   float another_value = 3 [(buf.validate.field).float = { gt: 10.0, lt: 5.0 }];\n    // }\n    // ```\n    float gt = 4 [\n      (predefined).cel = {\n        id: \"float.gt\"\n        expression:\n            \"!has(rules.lt) && !has(rules.lte) && (this.isNan() || this <= rules.gt)\"\n                \"? 'value must be greater than %s'.format([rules.gt]) : ''\"\n      },\n      (predefined).cel = {\n        id: \"float.gt_lt\"\n        expression:\n            \"has(rules.lt) && rules.lt >= rules.gt && (this.isNan() || this >= rules.lt || this <= rules.gt)\"\n                \"? 'value must be greater than %s and less than %s'.format([rules.gt, rules.lt]) : ''\"\n      },\n      (predefined).cel = {\n        id: \"float.gt_lt_exclusive\"\n        expression:\n            \"has(rules.lt) && rules.lt < rules.gt && (this.isNan() || (rules.lt <= this && this <= rules.gt))\"\n                \"? 'value must be greater than %s or less than %s'.format([rules.gt, rules.lt]) : ''\"\n      },\n      (predefined).cel = {\n        id: \"float.gt_lte\"\n        expression:\n            \"has(rules.lte) && rules.lte >= rules.gt && (this.isNan() || this > rules.lte || this <= rules.gt)\"\n                \"? 'value must be greater than %s and less than or equal to %s'.format([rules.gt, rules.lte]) : ''\"\n      },\n      (predefined).cel = {\n        id: \"float.gt_lte_exclusive\"\n        expression:\n            \"has(rules.lte) && rules.lte < rules.gt && (this.isNan() || (rules.lte < this && this <= rules.gt))\"\n                \"? 'value must be greater than %s or less than or equal to %s'.format([rules.gt, rules.lte]) : ''\"\n      }\n    ];\n\n    // `gte` requires the field value to be greater than or equal to the specified\n    // value (exclusive). If the value of `gte` is larger than a specified `lt`\n    // or `lte`, the range is reversed, and the field value must be outside the\n    // specified range. If the field value doesn't meet the required conditions,\n    // an error message is generated.\n    //\n    // ```proto\n    // message MyFloat {\n    //   // value must be greater than or equal to 5.0 [float.gte]\n    //   float value = 1 [(buf.validate.field).float.gte = 5.0];\n    //\n    //   // value must be greater than or equal to 5.0 and less than 10.0 [float.gte_lt]\n    //   float other_value = 2 [(buf.validate.field).float = { gte: 5.0, lt: 10.0 }];\n    //\n    //   // value must be greater than or equal to 10.0 or less than 5.0 [float.gte_lt_exclusive]\n    //   float another_value = 3 [(buf.validate.field).float = { gte: 10.0, lt: 5.0 }];\n    // }\n    // ```\n    float gte = 5 [\n      (predefined).cel = {\n        id: \"float.gte\"\n        expression:\n            \"!has(rules.lt) && !has(rules.lte) && (this.isNan() || this < rules.gte)\"\n                \"? 'value must be greater than or equal to %s'.format([rules.gte]) : ''\"\n      },\n      (predefined).cel = {\n        id: \"float.gte_lt\"\n        expression:\n            \"has(rules.lt) && rules.lt >= rules.gte && (this.isNan() || this >= rules.lt || this < rules.gte)\"\n                \"? 'value must be greater than or equal to %s and less than %s'.format([rules.gte, rules.lt]) : ''\"\n      },\n      (predefined).cel = {\n        id: \"float.gte_lt_exclusive\"\n        expression:\n            \"has(rules.lt) && rules.lt < rules.gte && (this.isNan() || (rules.lt <= this && this < rules.gte))\"\n                \"? 'value must be greater than or equal to %s or less than %s'.format([rules.gte, rules.lt]) : ''\"\n      },\n      (predefined).cel = {\n        id: \"float.gte_lte\"\n        expression:\n            \"has(rules.lte) && rules.lte >= rules.gte && (this.isNan() || this > rules.lte || this < rules.gte)\"\n                \"? 'value must be greater than or equal to %s and less than or equal to %s'.format([rules.gte, rules.lte]) : ''\"\n      },\n      (predefined).cel = {\n        id: \"float.gte_lte_exclusive\"\n        expression:\n            \"has(rules.lte) && rules.lte < rules.gte && (this.isNan() || (rules.lte < this && this < rules.gte))\"\n                \"? 'value must be greater than or equal to %s or less than or equal to %s'.format([rules.gte, rules.lte]) : ''\"\n      }\n    ];\n  }\n\n  // `in` requires the field value to be equal to one of the specified values.\n  // If the field value isn't one of the specified values, an error message\n  // is generated.\n  //\n  // ```proto\n  // message MyFloat {\n  //   // value must be in list [1.0, 2.0, 3.0]\n  //   repeated float value = 1 (buf.validate.field).float = { in: [1.0, 2.0, 3.0] };\n  // }\n  // ```\n  repeated float in = 6 [(predefined).cel = {\n    id: \"float.in\"\n    expression: \"!(this in dyn(rules)['in']) ? 'value must be in list %s'.format([dyn(rules)['in']]) : ''\"\n  }];\n\n  // `in` requires the field value to not be equal to any of the specified\n  // values. If the field value is one of the specified values, an error\n  // message is generated.\n  //\n  // ```proto\n  // message MyFloat {\n  //   // value must not be in list [1.0, 2.0, 3.0]\n  //   repeated float value = 1 (buf.validate.field).float = { not_in: [1.0, 2.0, 3.0] };\n  // }\n  // ```\n  repeated float not_in = 7 [(predefined).cel = {\n    id: \"float.not_in\"\n    expression: \"this in rules.not_in ? 'value must not be in list %s'.format([rules.not_in]) : ''\"\n  }];\n\n  // `finite` requires the field value to be finite. If the field value is\n  // infinite or NaN, an error message is generated.\n  optional bool finite = 8 [(predefined).cel = {\n    id: \"float.finite\"\n    expression: \"rules.finite ? (this.isNan() || this.isInf() ? 'value must be finite' : '') : ''\"\n  }];\n\n  // `example` specifies values that the field may have. These values SHOULD\n  // conform to other constraints. `example` values will not impact validation\n  // but may be used as helpful guidance on how to populate the given field.\n  //\n  // ```proto\n  // message MyFloat {\n  //   float value = 1 [\n  //     (buf.validate.field).float.example = 1.0,\n  //     (buf.validate.field).float.example = \"Infinity\"\n  //   ];\n  // }\n  // ```\n  repeated float example = 9 [(predefined).cel = {\n    id: \"float.example\"\n    expression: \"true\"\n  }];\n\n  // Extension fields in this range that have the (buf.validate.predefined)\n  // option set will be treated as predefined field constraints that can then be\n  // set on the field options of other fields to apply field constraints.\n  // Extension numbers 1000 to 99999 are reserved for extension numbers that are\n  // defined in the [Protobuf Global Extension Registry][1]. Extension numbers\n  // above this range are reserved for extension numbers that are not explicitly\n  // assigned. For rules defined in publicly-consumed schemas, use of extensions\n  // above 99999 is discouraged due to the risk of conflicts.\n  //\n  // [1]: https://github.com/protocolbuffers/protobuf/blob/main/docs/options.md\n  extensions 1000 to max;\n}\n\n// DoubleRules describes the constraints applied to `double` values. These\n// rules may also be applied to the `google.protobuf.DoubleValue` Well-Known-Type.\nmessage DoubleRules {\n  // `const` requires the field value to exactly match the specified value. If\n  // the field value doesn't match, an error message is generated.\n  //\n  // ```proto\n  // message MyDouble {\n  //   // value must equal 42.0\n  //   double value = 1 [(buf.validate.field).double.const = 42.0];\n  // }\n  // ```\n  optional double const = 1 [(predefined).cel = {\n    id: \"double.const\"\n    expression: \"this != rules.const ? 'value must equal %s'.format([rules.const]) : ''\"\n  }];\n  oneof less_than {\n    // `lt` requires the field value to be less than the specified value (field <\n    // value). If the field value is equal to or greater than the specified\n    // value, an error message is generated.\n    //\n    // ```proto\n    // message MyDouble {\n    //   // value must be less than 10.0\n    //   double value = 1 [(buf.validate.field).double.lt = 10.0];\n    // }\n    // ```\n    double lt = 2 [(predefined).cel = {\n      id: \"double.lt\"\n      expression:\n          \"!has(rules.gte) && !has(rules.gt) && (this.isNan() || this >= rules.lt)\"\n              \"? 'value must be less than %s'.format([rules.lt]) : ''\"\n    }];\n\n    // `lte` requires the field value to be less than or equal to the specified value\n    // (field <= value). If the field value is greater than the specified value,\n    // an error message is generated.\n    //\n    // ```proto\n    // message MyDouble {\n    //   // value must be less than or equal to 10.0\n    //   double value = 1 [(buf.validate.field).double.lte = 10.0];\n    // }\n    // ```\n    double lte = 3 [(predefined).cel = {\n      id: \"double.lte\"\n      expression:\n          \"!has(rules.gte) && !has(rules.gt) && (this.isNan() || this > rules.lte)\"\n              \"? 'value must be less than or equal to %s'.format([rules.lte]) : ''\"\n    }];\n  }\n  oneof greater_than {\n    // `gt` requires the field value to be greater than the specified value\n    // (exclusive). If the value of `gt` is larger than a specified `lt` or `lte`,\n    // the range is reversed, and the field value must be outside the specified\n    // range. If the field value doesn't meet the required conditions, an error\n    // message is generated.\n    //\n    // ```proto\n    // message MyDouble {\n    //   // value must be greater than 5.0 [double.gt]\n    //   double value = 1 [(buf.validate.field).double.gt = 5.0];\n    //\n    //   // value must be greater than 5 and less than 10.0 [double.gt_lt]\n    //   double other_value = 2 [(buf.validate.field).double = { gt: 5.0, lt: 10.0 }];\n    //\n    //   // value must be greater than 10 or less than 5.0 [double.gt_lt_exclusive]\n    //   double another_value = 3 [(buf.validate.field).double = { gt: 10.0, lt: 5.0 }];\n    // }\n    // ```\n    double gt = 4 [\n      (predefined).cel = {\n        id: \"double.gt\"\n        expression:\n            \"!has(rules.lt) && !has(rules.lte) && (this.isNan() || this <= rules.gt)\"\n                \"? 'value must be greater than %s'.format([rules.gt]) : ''\"\n      },\n      (predefined).cel = {\n        id: \"double.gt_lt\"\n        expression:\n            \"has(rules.lt) && rules.lt >= rules.gt && (this.isNan() || this >= rules.lt || this <= rules.gt)\"\n                \"? 'value must be greater than %s and less than %s'.format([rules.gt, rules.lt]) : ''\"\n      },\n      (predefined).cel = {\n        id: \"double.gt_lt_exclusive\"\n        expression:\n            \"has(rules.lt) && rules.lt < rules.gt && (this.isNan() || (rules.lt <= this && this <= rules.gt))\"\n                \"? 'value must be greater than %s or less than %s'.format([rules.gt, rules.lt]) : ''\"\n      },\n      (predefined).cel = {\n        id: \"double.gt_lte\"\n        expression:\n            \"has(rules.lte) && rules.lte >= rules.gt && (this.isNan() || this > rules.lte || this <= rules.gt)\"\n                \"? 'value must be greater than %s and less than or equal to %s'.format([rules.gt, rules.lte]) : ''\"\n      },\n      (predefined).cel = {\n        id: \"double.gt_lte_exclusive\"\n        expression:\n            \"has(rules.lte) && rules.lte < rules.gt && (this.isNan() || (rules.lte < this && this <= rules.gt))\"\n                \"? 'value must be greater than %s or less than or equal to %s'.format([rules.gt, rules.lte]) : ''\"\n      }\n    ];\n\n    // `gte` requires the field value to be greater than or equal to the specified\n    // value (exclusive). If the value of `gte` is larger than a specified `lt` or\n    // `lte`, the range is reversed, and the field value must be outside the\n    // specified range. If the field value doesn't meet the required conditions,\n    // an error message is generated.\n    //\n    // ```proto\n    // message MyDouble {\n    //   // value must be greater than or equal to 5.0 [double.gte]\n    //   double value = 1 [(buf.validate.field).double.gte = 5.0];\n    //\n    //   // value must be greater than or equal to 5.0 and less than 10.0 [double.gte_lt]\n    //   double other_value = 2 [(buf.validate.field).double = { gte: 5.0, lt: 10.0 }];\n    //\n    //   // value must be greater than or equal to 10.0 or less than 5.0 [double.gte_lt_exclusive]\n    //   double another_value = 3 [(buf.validate.field).double = { gte: 10.0, lt: 5.0 }];\n    // }\n    // ```\n    double gte = 5 [\n      (predefined).cel = {\n        id: \"double.gte\"\n        expression:\n            \"!has(rules.lt) && !has(rules.lte) && (this.isNan() || this < rules.gte)\"\n                \"? 'value must be greater than or equal to %s'.format([rules.gte]) : ''\"\n      },\n      (predefined).cel = {\n        id: \"double.gte_lt\"\n        expression:\n            \"has(rules.lt) && rules.lt >= rules.gte && (this.isNan() || this >= rules.lt || this < rules.gte)\"\n                \"? 'value must be greater than or equal to %s and less than %s'.format([rules.gte, rules.lt]) : ''\"\n      },\n      (predefined).cel = {\n        id: \"double.gte_lt_exclusive\"\n        expression:\n            \"has(rules.lt) && rules.lt < rules.gte && (this.isNan() || (rules.lt <= this && this < rules.gte))\"\n                \"? 'value must be greater than or equal to %s or less than %s'.format([rules.gte, rules.lt]) : ''\"\n      },\n      (predefined).cel = {\n        id: \"double.gte_lte\"\n        expression:\n            \"has(rules.lte) && rules.lte >= rules.gte && (this.isNan() || this > rules.lte || this < rules.gte)\"\n                \"? 'value must be greater than or equal to %s and less than or equal to %s'.format([rules.gte, rules.lte]) : ''\"\n      },\n      (predefined).cel = {\n        id: \"double.gte_lte_exclusive\"\n        expression:\n            \"has(rules.lte) && rules.lte < rules.gte && (this.isNan() || (rules.lte < this && this < rules.gte))\"\n                \"? 'value must be greater than or equal to %s or less than or equal to %s'.format([rules.gte, rules.lte]) : ''\"\n      }\n    ];\n  }\n  // `in` requires the field value to be equal to one of the specified values.\n  // If the field value isn't one of the specified values, an error message is\n  // generated.\n  //\n  // ```proto\n  // message MyDouble {\n  //   // value must be in list [1.0, 2.0, 3.0]\n  //   repeated double value = 1 (buf.validate.field).double = { in: [1.0, 2.0, 3.0] };\n  // }\n  // ```\n  repeated double in = 6 [(predefined).cel = {\n    id: \"double.in\"\n    expression: \"!(this in dyn(rules)['in']) ? 'value must be in list %s'.format([dyn(rules)['in']]) : ''\"\n  }];\n\n  // `not_in` requires the field value to not be equal to any of the specified\n  // values. If the field value is one of the specified values, an error\n  // message is generated.\n  //\n  // ```proto\n  // message MyDouble {\n  //   // value must not be in list [1.0, 2.0, 3.0]\n  //   repeated double value = 1 (buf.validate.field).double = { not_in: [1.0, 2.0, 3.0] };\n  // }\n  // ```\n  repeated double not_in = 7 [(predefined).cel = {\n    id: \"double.not_in\"\n    expression: \"this in rules.not_in ? 'value must not be in list %s'.format([rules.not_in]) : ''\"\n  }];\n\n  // `finite` requires the field value to be finite. If the field value is\n  // infinite or NaN, an error message is generated.\n  optional bool finite = 8 [(predefined).cel = {\n    id: \"double.finite\"\n    expression: \"rules.finite ? (this.isNan() || this.isInf() ? 'value must be finite' : '') : ''\"\n  }];\n\n  // `example` specifies values that the field may have. These values SHOULD\n  // conform to other constraints. `example` values will not impact validation\n  // but may be used as helpful guidance on how to populate the given field.\n  //\n  // ```proto\n  // message MyDouble {\n  //   double value = 1 [\n  //     (buf.validate.field).double.example = 1.0,\n  //     (buf.validate.field).double.example = \"Infinity\"\n  //   ];\n  // }\n  // ```\n  repeated double example = 9 [(predefined).cel = {\n    id: \"double.example\"\n    expression: \"true\"\n  }];\n\n  // Extension fields in this range that have the (buf.validate.predefined)\n  // option set will be treated as predefined field constraints that can then be\n  // set on the field options of other fields to apply field constraints.\n  // Extension numbers 1000 to 99999 are reserved for extension numbers that are\n  // defined in the [Protobuf Global Extension Registry][1]. Extension numbers\n  // above this range are reserved for extension numbers that are not explicitly\n  // assigned. For rules defined in publicly-consumed schemas, use of extensions\n  // above 99999 is discouraged due to the risk of conflicts.\n  //\n  // [1]: https://github.com/protocolbuffers/protobuf/blob/main/docs/options.md\n  extensions 1000 to max;\n}\n\n// Int32Rules describes the constraints applied to `int32` values. These\n// rules may also be applied to the `google.protobuf.Int32Value` Well-Known-Type.\nmessage Int32Rules {\n  // `const` requires the field value to exactly match the specified value. If\n  // the field value doesn't match, an error message is generated.\n  //\n  // ```proto\n  // message MyInt32 {\n  //   // value must equal 42\n  //   int32 value = 1 [(buf.validate.field).int32.const = 42];\n  // }\n  // ```\n  optional int32 const = 1 [(predefined).cel = {\n    id: \"int32.const\"\n    expression: \"this != rules.const ? 'value must equal %s'.format([rules.const]) : ''\"\n  }];\n  oneof less_than {\n    // `lt` requires the field value to be less than the specified value (field\n    // < value). If the field value is equal to or greater than the specified\n    // value, an error message is generated.\n    //\n    // ```proto\n    // message MyInt32 {\n    //   // value must be less than 10\n    //   int32 value = 1 [(buf.validate.field).int32.lt = 10];\n    // }\n    // ```\n    int32 lt = 2 [(predefined).cel = {\n      id: \"int32.lt\"\n      expression:\n          \"!has(rules.gte) && !has(rules.gt) && this >= rules.lt\"\n              \"? 'value must be less than %s'.format([rules.lt]) : ''\"\n    }];\n\n    // `lte` requires the field value to be less than or equal to the specified\n    // value (field <= value). If the field value is greater than the specified\n    // value, an error message is generated.\n    //\n    // ```proto\n    // message MyInt32 {\n    //   // value must be less than or equal to 10\n    //   int32 value = 1 [(buf.validate.field).int32.lte = 10];\n    // }\n    // ```\n    int32 lte = 3 [(predefined).cel = {\n      id: \"int32.lte\"\n      expression:\n          \"!has(rules.gte) && !has(rules.gt) && this > rules.lte\"\n              \"? 'value must be less than or equal to %s'.format([rules.lte]) : ''\"\n    }];\n  }\n  oneof greater_than {\n    // `gt` requires the field value to be greater than the specified value\n    // (exclusive). If the value of `gt` is larger than a specified `lt` or\n    // `lte`, the range is reversed, and the field value must be outside the\n    // specified range. If the field value doesn't meet the required conditions,\n    // an error message is generated.\n    //\n    // ```proto\n    // message MyInt32 {\n    //   // value must be greater than 5 [int32.gt]\n    //   int32 value = 1 [(buf.validate.field).int32.gt = 5];\n    //\n    //   // value must be greater than 5 and less than 10 [int32.gt_lt]\n    //   int32 other_value = 2 [(buf.validate.field).int32 = { gt: 5, lt: 10 }];\n    //\n    //   // value must be greater than 10 or less than 5 [int32.gt_lt_exclusive]\n    //   int32 another_value = 3 [(buf.validate.field).int32 = { gt: 10, lt: 5 }];\n    // }\n    // ```\n    int32 gt = 4 [\n      (predefined).cel = {\n        id: \"int32.gt\"\n        expression:\n            \"!has(rules.lt) && !has(rules.lte) && this <= rules.gt\"\n                \"? 'value must be greater than %s'.format([rules.gt]) : ''\"\n      },\n      (predefined).cel = {\n        id: \"int32.gt_lt\"\n        expression:\n            \"has(rules.lt) && rules.lt >= rules.gt && (this >= rules.lt || this <= rules.gt)\"\n                \"? 'value must be greater than %s and less than %s'.format([rules.gt, rules.lt]) : ''\"\n      },\n      (predefined).cel = {\n        id: \"int32.gt_lt_exclusive\"\n        expression:\n            \"has(rules.lt) && rules.lt < rules.gt && (rules.lt <= this && this <= rules.gt)\"\n                \"? 'value must be greater than %s or less than %s'.format([rules.gt, rules.lt]) : ''\"\n      },\n      (predefined).cel = {\n        id: \"int32.gt_lte\"\n        expression:\n            \"has(rules.lte) && rules.lte >= rules.gt && (this > rules.lte || this <= rules.gt)\"\n                \"? 'value must be greater than %s and less than or equal to %s'.format([rules.gt, rules.lte]) : ''\"\n      },\n      (predefined).cel = {\n        id: \"int32.gt_lte_exclusive\"\n        expression:\n            \"has(rules.lte) && rules.lte < rules.gt && (rules.lte < this && this <= rules.gt)\"\n                \"? 'value must be greater than %s or less than or equal to %s'.format([rules.gt, rules.lte]) : ''\"\n      }\n    ];\n\n    // `gte` requires the field value to be greater than or equal to the specified value\n    // (exclusive). If the value of `gte` is larger than a specified `lt` or\n    // `lte`, the range is reversed, and the field value must be outside the\n    // specified range. If the field value doesn't meet the required conditions,\n    // an error message is generated.\n    //\n    // ```proto\n    // message MyInt32 {\n    //   // value must be greater than or equal to 5 [int32.gte]\n    //   int32 value = 1 [(buf.validate.field).int32.gte = 5];\n    //\n    //   // value must be greater than or equal to 5 and less than 10 [int32.gte_lt]\n    //   int32 other_value = 2 [(buf.validate.field).int32 = { gte: 5, lt: 10 }];\n    //\n    //   // value must be greater than or equal to 10 or less than 5 [int32.gte_lt_exclusive]\n    //   int32 another_value = 3 [(buf.validate.field).int32 = { gte: 10, lt: 5 }];\n    // }\n    // ```\n    int32 gte = 5 [\n      (predefined).cel = {\n        id: \"int32.gte\"\n        expression:\n            \"!has(rules.lt) && !has(rules.lte) && this < rules.gte\"\n                \"? 'value must be greater than or equal to %s'.format([rules.gte]) : ''\"\n      },\n      (predefined).cel = {\n        id: \"int32.gte_lt\"\n        expression:\n            \"has(rules.lt) && rules.lt >= rules.gte && (this >= rules.lt || this < rules.gte)\"\n                \"? 'value must be greater than or equal to %s and less than %s'.format([rules.gte, rules.lt]) : ''\"\n      },\n      (predefined).cel = {\n        id: \"int32.gte_lt_exclusive\"\n        expression:\n            \"has(rules.lt) && rules.lt < rules.gte && (rules.lt <= this && this < rules.gte)\"\n                \"? 'value must be greater than or equal to %s or less than %s'.format([rules.gte, rules.lt]) : ''\"\n      },\n      (predefined).cel = {\n        id: \"int32.gte_lte\"\n        expression:\n            \"has(rules.lte) && rules.lte >= rules.gte && (this > rules.lte || this < rules.gte)\"\n                \"? 'value must be greater than or equal to %s and less than or equal to %s'.format([rules.gte, rules.lte]) : ''\"\n      },\n      (predefined).cel = {\n        id: \"int32.gte_lte_exclusive\"\n        expression:\n            \"has(rules.lte) && rules.lte < rules.gte && (rules.lte < this && this < rules.gte)\"\n                \"? 'value must be greater than or equal to %s or less than or equal to %s'.format([rules.gte, rules.lte]) : ''\"\n      }\n    ];\n  }\n\n  // `in` requires the field value to be equal to one of the specified values.\n  // If the field value isn't one of the specified values, an error message is\n  // generated.\n  //\n  // ```proto\n  // message MyInt32 {\n  //   // value must be in list [1, 2, 3]\n  //   repeated int32 value = 1 (buf.validate.field).int32 = { in: [1, 2, 3] };\n  // }\n  // ```\n  repeated int32 in = 6 [(predefined).cel = {\n    id: \"int32.in\"\n    expression: \"!(this in dyn(rules)['in']) ? 'value must be in list %s'.format([dyn(rules)['in']]) : ''\"\n  }];\n\n  // `not_in` requires the field value to not be equal to any of the specified\n  // values. If the field value is one of the specified values, an error message\n  // is generated.\n  //\n  // ```proto\n  // message MyInt32 {\n  //   // value must not be in list [1, 2, 3]\n  //   repeated int32 value = 1 (buf.validate.field).int32 = { not_in: [1, 2, 3] };\n  // }\n  // ```\n  repeated int32 not_in = 7 [(predefined).cel = {\n    id: \"int32.not_in\"\n    expression: \"this in rules.not_in ? 'value must not be in list %s'.format([rules.not_in]) : ''\"\n  }];\n\n  // `example` specifies values that the field may have. These values SHOULD\n  // conform to other constraints. `example` values will not impact validation\n  // but may be used as helpful guidance on how to populate the given field.\n  //\n  // ```proto\n  // message MyInt32 {\n  //   int32 value = 1 [\n  //     (buf.validate.field).int32.example = 1,\n  //     (buf.validate.field).int32.example = -10\n  //   ];\n  // }\n  // ```\n  repeated int32 example = 8 [(predefined).cel = {\n    id: \"int32.example\"\n    expression: \"true\"\n  }];\n\n  // Extension fields in this range that have the (buf.validate.predefined)\n  // option set will be treated as predefined field constraints that can then be\n  // set on the field options of other fields to apply field constraints.\n  // Extension numbers 1000 to 99999 are reserved for extension numbers that are\n  // defined in the [Protobuf Global Extension Registry][1]. Extension numbers\n  // above this range are reserved for extension numbers that are not explicitly\n  // assigned. For rules defined in publicly-consumed schemas, use of extensions\n  // above 99999 is discouraged due to the risk of conflicts.\n  //\n  // [1]: https://github.com/protocolbuffers/protobuf/blob/main/docs/options.md\n  extensions 1000 to max;\n}\n\n// Int64Rules describes the constraints applied to `int64` values. These\n// rules may also be applied to the `google.protobuf.Int64Value` Well-Known-Type.\nmessage Int64Rules {\n  // `const` requires the field value to exactly match the specified value. If\n  // the field value doesn't match, an error message is generated.\n  //\n  // ```proto\n  // message MyInt64 {\n  //   // value must equal 42\n  //   int64 value = 1 [(buf.validate.field).int64.const = 42];\n  // }\n  // ```\n  optional int64 const = 1 [(predefined).cel = {\n    id: \"int64.const\"\n    expression: \"this != rules.const ? 'value must equal %s'.format([rules.const]) : ''\"\n  }];\n  oneof less_than {\n    // `lt` requires the field value to be less than the specified value (field <\n    // value). If the field value is equal to or greater than the specified value,\n    // an error message is generated.\n    //\n    // ```proto\n    // message MyInt64 {\n    //   // value must be less than 10\n    //   int64 value = 1 [(buf.validate.field).int64.lt = 10];\n    // }\n    // ```\n    int64 lt = 2 [(predefined).cel = {\n      id: \"int64.lt\"\n      expression:\n          \"!has(rules.gte) && !has(rules.gt) && this >= rules.lt\"\n              \"? 'value must be less than %s'.format([rules.lt]) : ''\"\n    }];\n\n    // `lte` requires the field value to be less than or equal to the specified\n    // value (field <= value). If the field value is greater than the specified\n    // value, an error message is generated.\n    //\n    // ```proto\n    // message MyInt64 {\n    //   // value must be less than or equal to 10\n    //   int64 value = 1 [(buf.validate.field).int64.lte = 10];\n    // }\n    // ```\n    int64 lte = 3 [(predefined).cel = {\n      id: \"int64.lte\"\n      expression:\n          \"!has(rules.gte) && !has(rules.gt) && this > rules.lte\"\n              \"? 'value must be less than or equal to %s'.format([rules.lte]) : ''\"\n    }];\n  }\n  oneof greater_than {\n    // `gt` requires the field value to be greater than the specified value\n    // (exclusive). If the value of `gt` is larger than a specified `lt` or\n    // `lte`, the range is reversed, and the field value must be outside the\n    // specified range. If the field value doesn't meet the required conditions,\n    // an error message is generated.\n    //\n    // ```proto\n    // message MyInt64 {\n    //   // value must be greater than 5 [int64.gt]\n    //   int64 value = 1 [(buf.validate.field).int64.gt = 5];\n    //\n    //   // value must be greater than 5 and less than 10 [int64.gt_lt]\n    //   int64 other_value = 2 [(buf.validate.field).int64 = { gt: 5, lt: 10 }];\n    //\n    //   // value must be greater than 10 or less than 5 [int64.gt_lt_exclusive]\n    //   int64 another_value = 3 [(buf.validate.field).int64 = { gt: 10, lt: 5 }];\n    // }\n    // ```\n    int64 gt = 4 [\n      (predefined).cel = {\n        id: \"int64.gt\"\n        expression:\n            \"!has(rules.lt) && !has(rules.lte) && this <= rules.gt\"\n                \"? 'value must be greater than %s'.format([rules.gt]) : ''\"\n      },\n      (predefined).cel = {\n        id: \"int64.gt_lt\"\n        expression:\n            \"has(rules.lt) && rules.lt >= rules.gt && (this >= rules.lt || this <= rules.gt)\"\n                \"? 'value must be greater than %s and less than %s'.format([rules.gt, rules.lt]) : ''\"\n      },\n      (predefined).cel = {\n        id: \"int64.gt_lt_exclusive\"\n        expression:\n            \"has(rules.lt) && rules.lt < rules.gt && (rules.lt <= this && this <= rules.gt)\"\n                \"? 'value must be greater than %s or less than %s'.format([rules.gt, rules.lt]) : ''\"\n      },\n      (predefined).cel = {\n        id: \"int64.gt_lte\"\n        expression:\n            \"has(rules.lte) && rules.lte >= rules.gt && (this > rules.lte || this <= rules.gt)\"\n                \"? 'value must be greater than %s and less than or equal to %s'.format([rules.gt, rules.lte]) : ''\"\n      },\n      (predefined).cel = {\n        id: \"int64.gt_lte_exclusive\"\n        expression:\n            \"has(rules.lte) && rules.lte < rules.gt && (rules.lte < this && this <= rules.gt)\"\n                \"? 'value must be greater than %s or less than or equal to %s'.format([rules.gt, rules.lte]) : ''\"\n      }\n    ];\n\n    // `gte` requires the field value to be greater than or equal to the specified\n    // value (exclusive). If the value of `gte` is larger than a specified `lt`\n    // or `lte`, the range is reversed, and the field value must be outside the\n    // specified range. If the field value doesn't meet the required conditions,\n    // an error message is generated.\n    //\n    // ```proto\n    // message MyInt64 {\n    //   // value must be greater than or equal to 5 [int64.gte]\n    //   int64 value = 1 [(buf.validate.field).int64.gte = 5];\n    //\n    //   // value must be greater than or equal to 5 and less than 10 [int64.gte_lt]\n    //   int64 other_value = 2 [(buf.validate.field).int64 = { gte: 5, lt: 10 }];\n    //\n    //   // value must be greater than or equal to 10 or less than 5 [int64.gte_lt_exclusive]\n    //   int64 another_value = 3 [(buf.validate.field).int64 = { gte: 10, lt: 5 }];\n    // }\n    // ```\n    int64 gte = 5 [\n      (predefined).cel = {\n        id: \"int64.gte\"\n        expression:\n            \"!has(rules.lt) && !has(rules.lte) && this < rules.gte\"\n                \"? 'value must be greater than or equal to %s'.format([rules.gte]) : ''\"\n      },\n      (predefined).cel = {\n        id: \"int64.gte_lt\"\n        expression:\n            \"has(rules.lt) && rules.lt >= rules.gte && (this >= rules.lt || this < rules.gte)\"\n                \"? 'value must be greater than or equal to %s and less than %s'.format([rules.gte, rules.lt]) : ''\"\n      },\n      (predefined).cel = {\n        id: \"int64.gte_lt_exclusive\"\n        expression:\n            \"has(rules.lt) && rules.lt < rules.gte && (rules.lt <= this && this < rules.gte)\"\n                \"? 'value must be greater than or equal to %s or less than %s'.format([rules.gte, rules.lt]) : ''\"\n      },\n      (predefined).cel = {\n        id: \"int64.gte_lte\"\n        expression:\n            \"has(rules.lte) && rules.lte >= rules.gte && (this > rules.lte || this < rules.gte)\"\n                \"? 'value must be greater than or equal to %s and less than or equal to %s'.format([rules.gte, rules.lte]) : ''\"\n      },\n      (predefined).cel = {\n        id: \"int64.gte_lte_exclusive\"\n        expression:\n            \"has(rules.lte) && rules.lte < rules.gte && (rules.lte < this && this < rules.gte)\"\n                \"? 'value must be greater than or equal to %s or less than or equal to %s'.format([rules.gte, rules.lte]) : ''\"\n      }\n    ];\n  }\n\n  // `in` requires the field value to be equal to one of the specified values.\n  // If the field value isn't one of the specified values, an error message is\n  // generated.\n  //\n  // ```proto\n  // message MyInt64 {\n  //   // value must be in list [1, 2, 3]\n  //   repeated int64 value = 1 (buf.validate.field).int64 = { in: [1, 2, 3] };\n  // }\n  // ```\n  repeated int64 in = 6 [(predefined).cel = {\n    id: \"int64.in\"\n    expression: \"!(this in dyn(rules)['in']) ? 'value must be in list %s'.format([dyn(rules)['in']]) : ''\"\n  }];\n\n  // `not_in` requires the field value to not be equal to any of the specified\n  // values. If the field value is one of the specified values, an error\n  // message is generated.\n  //\n  // ```proto\n  // message MyInt64 {\n  //   // value must not be in list [1, 2, 3]\n  //   repeated int64 value = 1 (buf.validate.field).int64 = { not_in: [1, 2, 3] };\n  // }\n  // ```\n  repeated int64 not_in = 7 [(predefined).cel = {\n    id: \"int64.not_in\"\n    expression: \"this in rules.not_in ? 'value must not be in list %s'.format([rules.not_in]) : ''\"\n  }];\n\n  // `example` specifies values that the field may have. These values SHOULD\n  // conform to other constraints. `example` values will not impact validation\n  // but may be used as helpful guidance on how to populate the given field.\n  //\n  // ```proto\n  // message MyInt64 {\n  //   int64 value = 1 [\n  //     (buf.validate.field).int64.example = 1,\n  //     (buf.validate.field).int64.example = -10\n  //   ];\n  // }\n  // ```\n  repeated int64 example = 9 [(predefined).cel = {\n    id: \"int64.example\"\n    expression: \"true\"\n  }];\n\n  // Extension fields in this range that have the (buf.validate.predefined)\n  // option set will be treated as predefined field constraints that can then be\n  // set on the field options of other fields to apply field constraints.\n  // Extension numbers 1000 to 99999 are reserved for extension numbers that are\n  // defined in the [Protobuf Global Extension Registry][1]. Extension numbers\n  // above this range are reserved for extension numbers that are not explicitly\n  // assigned. For rules defined in publicly-consumed schemas, use of extensions\n  // above 99999 is discouraged due to the risk of conflicts.\n  //\n  // [1]: https://github.com/protocolbuffers/protobuf/blob/main/docs/options.md\n  extensions 1000 to max;\n}\n\n// UInt32Rules describes the constraints applied to `uint32` values. These\n// rules may also be applied to the `google.protobuf.UInt32Value` Well-Known-Type.\nmessage UInt32Rules {\n  // `const` requires the field value to exactly match the specified value. If\n  // the field value doesn't match, an error message is generated.\n  //\n  // ```proto\n  // message MyUInt32 {\n  //   // value must equal 42\n  //   uint32 value = 1 [(buf.validate.field).uint32.const = 42];\n  // }\n  // ```\n  optional uint32 const = 1 [(predefined).cel = {\n    id: \"uint32.const\"\n    expression: \"this != rules.const ? 'value must equal %s'.format([rules.const]) : ''\"\n  }];\n  oneof less_than {\n    // `lt` requires the field value to be less than the specified value (field <\n    // value). If the field value is equal to or greater than the specified value,\n    // an error message is generated.\n    //\n    // ```proto\n    // message MyUInt32 {\n    //   // value must be less than 10\n    //   uint32 value = 1 [(buf.validate.field).uint32.lt = 10];\n    // }\n    // ```\n    uint32 lt = 2 [(predefined).cel = {\n      id: \"uint32.lt\"\n      expression:\n          \"!has(rules.gte) && !has(rules.gt) && this >= rules.lt\"\n              \"? 'value must be less than %s'.format([rules.lt]) : ''\"\n    }];\n\n    // `lte` requires the field value to be less than or equal to the specified\n    // value (field <= value). If the field value is greater than the specified\n    // value, an error message is generated.\n    //\n    // ```proto\n    // message MyUInt32 {\n    //   // value must be less than or equal to 10\n    //   uint32 value = 1 [(buf.validate.field).uint32.lte = 10];\n    // }\n    // ```\n    uint32 lte = 3 [(predefined).cel = {\n      id: \"uint32.lte\"\n      expression:\n          \"!has(rules.gte) && !has(rules.gt) && this > rules.lte\"\n              \"? 'value must be less than or equal to %s'.format([rules.lte]) : ''\"\n    }];\n  }\n  oneof greater_than {\n    // `gt` requires the field value to be greater than the specified value\n    // (exclusive). If the value of `gt` is larger than a specified `lt` or\n    // `lte`, the range is reversed, and the field value must be outside the\n    // specified range. If the field value doesn't meet the required conditions,\n    // an error message is generated.\n    //\n    // ```proto\n    // message MyUInt32 {\n    //   // value must be greater than 5 [uint32.gt]\n    //   uint32 value = 1 [(buf.validate.field).uint32.gt = 5];\n    //\n    //   // value must be greater than 5 and less than 10 [uint32.gt_lt]\n    //   uint32 other_value = 2 [(buf.validate.field).uint32 = { gt: 5, lt: 10 }];\n    //\n    //   // value must be greater than 10 or less than 5 [uint32.gt_lt_exclusive]\n    //   uint32 another_value = 3 [(buf.validate.field).uint32 = { gt: 10, lt: 5 }];\n    // }\n    // ```\n    uint32 gt = 4 [\n      (predefined).cel = {\n        id: \"uint32.gt\"\n        expression:\n            \"!has(rules.lt) && !has(rules.lte) && this <= rules.gt\"\n                \"? 'value must be greater than %s'.format([rules.gt]) : ''\"\n      },\n      (predefined).cel = {\n        id: \"uint32.gt_lt\"\n        expression:\n            \"has(rules.lt) && rules.lt >= rules.gt && (this >= rules.lt || this <= rules.gt)\"\n                \"? 'value must be greater than %s and less than %s'.format([rules.gt, rules.lt]) : ''\"\n      },\n      (predefined).cel = {\n        id: \"uint32.gt_lt_exclusive\"\n        expression:\n            \"has(rules.lt) && rules.lt < rules.gt && (rules.lt <= this && this <= rules.gt)\"\n                \"? 'value must be greater than %s or less than %s'.format([rules.gt, rules.lt]) : ''\"\n      },\n      (predefined).cel = {\n        id: \"uint32.gt_lte\"\n        expression:\n            \"has(rules.lte) && rules.lte >= rules.gt && (this > rules.lte || this <= rules.gt)\"\n                \"? 'value must be greater than %s and less than or equal to %s'.format([rules.gt, rules.lte]) : ''\"\n      },\n      (predefined).cel = {\n        id: \"uint32.gt_lte_exclusive\"\n        expression:\n            \"has(rules.lte) && rules.lte < rules.gt && (rules.lte < this && this <= rules.gt)\"\n                \"? 'value must be greater than %s or less than or equal to %s'.format([rules.gt, rules.lte]) : ''\"\n      }\n    ];\n\n    // `gte` requires the field value to be greater than or equal to the specified\n    // value (exclusive). If the value of `gte` is larger than a specified `lt`\n    // or `lte`, the range is reversed, and the field value must be outside the\n    // specified range. If the field value doesn't meet the required conditions,\n    // an error message is generated.\n    //\n    // ```proto\n    // message MyUInt32 {\n    //   // value must be greater than or equal to 5 [uint32.gte]\n    //   uint32 value = 1 [(buf.validate.field).uint32.gte = 5];\n    //\n    //   // value must be greater than or equal to 5 and less than 10 [uint32.gte_lt]\n    //   uint32 other_value = 2 [(buf.validate.field).uint32 = { gte: 5, lt: 10 }];\n    //\n    //   // value must be greater than or equal to 10 or less than 5 [uint32.gte_lt_exclusive]\n    //   uint32 another_value = 3 [(buf.validate.field).uint32 = { gte: 10, lt: 5 }];\n    // }\n    // ```\n    uint32 gte = 5 [\n      (predefined).cel = {\n        id: \"uint32.gte\"\n        expression:\n            \"!has(rules.lt) && !has(rules.lte) && this < rules.gte\"\n                \"? 'value must be greater than or equal to %s'.format([rules.gte]) : ''\"\n      },\n      (predefined).cel = {\n        id: \"uint32.gte_lt\"\n        expression:\n            \"has(rules.lt) && rules.lt >= rules.gte && (this >= rules.lt || this < rules.gte)\"\n                \"? 'value must be greater than or equal to %s and less than %s'.format([rules.gte, rules.lt]) : ''\"\n      },\n      (predefined).cel = {\n        id: \"uint32.gte_lt_exclusive\"\n        expression:\n            \"has(rules.lt) && rules.lt < rules.gte && (rules.lt <= this && this < rules.gte)\"\n                \"? 'value must be greater than or equal to %s or less than %s'.format([rules.gte, rules.lt]) : ''\"\n      },\n      (predefined).cel = {\n        id: \"uint32.gte_lte\"\n        expression:\n            \"has(rules.lte) && rules.lte >= rules.gte && (this > rules.lte || this < rules.gte)\"\n                \"? 'value must be greater than or equal to %s and less than or equal to %s'.format([rules.gte, rules.lte]) : ''\"\n      },\n      (predefined).cel = {\n        id: \"uint32.gte_lte_exclusive\"\n        expression:\n            \"has(rules.lte) && rules.lte < rules.gte && (rules.lte < this && this < rules.gte)\"\n                \"? 'value must be greater than or equal to %s or less than or equal to %s'.format([rules.gte, rules.lte]) : ''\"\n      }\n    ];\n  }\n\n  // `in` requires the field value to be equal to one of the specified values.\n  // If the field value isn't one of the specified values, an error message is\n  // generated.\n  //\n  // ```proto\n  // message MyUInt32 {\n  //   // value must be in list [1, 2, 3]\n  //   repeated uint32 value = 1 (buf.validate.field).uint32 = { in: [1, 2, 3] };\n  // }\n  // ```\n  repeated uint32 in = 6 [(predefined).cel = {\n    id: \"uint32.in\"\n    expression: \"!(this in dyn(rules)['in']) ? 'value must be in list %s'.format([dyn(rules)['in']]) : ''\"\n  }];\n\n  // `not_in` requires the field value to not be equal to any of the specified\n  // values. If the field value is one of the specified values, an error\n  // message is generated.\n  //\n  // ```proto\n  // message MyUInt32 {\n  //   // value must not be in list [1, 2, 3]\n  //   repeated uint32 value = 1 (buf.validate.field).uint32 = { not_in: [1, 2, 3] };\n  // }\n  // ```\n  repeated uint32 not_in = 7 [(predefined).cel = {\n    id: \"uint32.not_in\"\n    expression: \"this in rules.not_in ? 'value must not be in list %s'.format([rules.not_in]) : ''\"\n  }];\n\n  // `example` specifies values that the field may have. These values SHOULD\n  // conform to other constraints. `example` values will not impact validation\n  // but may be used as helpful guidance on how to populate the given field.\n  //\n  // ```proto\n  // message MyUInt32 {\n  //   uint32 value = 1 [\n  //     (buf.validate.field).uint32.example = 1,\n  //     (buf.validate.field).uint32.example = 10\n  //   ];\n  // }\n  // ```\n  repeated uint32 example = 8 [(predefined).cel = {\n    id: \"uint32.example\"\n    expression: \"true\"\n  }];\n\n  // Extension fields in this range that have the (buf.validate.predefined)\n  // option set will be treated as predefined field constraints that can then be\n  // set on the field options of other fields to apply field constraints.\n  // Extension numbers 1000 to 99999 are reserved for extension numbers that are\n  // defined in the [Protobuf Global Extension Registry][1]. Extension numbers\n  // above this range are reserved for extension numbers that are not explicitly\n  // assigned. For rules defined in publicly-consumed schemas, use of extensions\n  // above 99999 is discouraged due to the risk of conflicts.\n  //\n  // [1]: https://github.com/protocolbuffers/protobuf/blob/main/docs/options.md\n  extensions 1000 to max;\n}\n\n// UInt64Rules describes the constraints applied to `uint64` values. These\n// rules may also be applied to the `google.protobuf.UInt64Value` Well-Known-Type.\nmessage UInt64Rules {\n  // `const` requires the field value to exactly match the specified value. If\n  // the field value doesn't match, an error message is generated.\n  //\n  // ```proto\n  // message MyUInt64 {\n  //   // value must equal 42\n  //   uint64 value = 1 [(buf.validate.field).uint64.const = 42];\n  // }\n  // ```\n  optional uint64 const = 1 [(predefined).cel = {\n    id: \"uint64.const\"\n    expression: \"this != rules.const ? 'value must equal %s'.format([rules.const]) : ''\"\n  }];\n  oneof less_than {\n    // `lt` requires the field value to be less than the specified value (field <\n    // value). If the field value is equal to or greater than the specified value,\n    // an error message is generated.\n    //\n    // ```proto\n    // message MyUInt64 {\n    //   // value must be less than 10\n    //   uint64 value = 1 [(buf.validate.field).uint64.lt = 10];\n    // }\n    // ```\n    uint64 lt = 2 [(predefined).cel = {\n      id: \"uint64.lt\"\n      expression:\n          \"!has(rules.gte) && !has(rules.gt) && this >= rules.lt\"\n              \"? 'value must be less than %s'.format([rules.lt]) : ''\"\n    }];\n\n    // `lte` requires the field value to be less than or equal to the specified\n    // value (field <= value). If the field value is greater than the specified\n    // value, an error message is generated.\n    //\n    // ```proto\n    // message MyUInt64 {\n    //   // value must be less than or equal to 10\n    //   uint64 value = 1 [(buf.validate.field).uint64.lte = 10];\n    // }\n    // ```\n    uint64 lte = 3 [(predefined).cel = {\n      id: \"uint64.lte\"\n      expression:\n          \"!has(rules.gte) && !has(rules.gt) && this > rules.lte\"\n              \"? 'value must be less than or equal to %s'.format([rules.lte]) : ''\"\n    }];\n  }\n  oneof greater_than {\n    // `gt` requires the field value to be greater than the specified value\n    // (exclusive). If the value of `gt` is larger than a specified `lt` or\n    // `lte`, the range is reversed, and the field value must be outside the\n    // specified range. If the field value doesn't meet the required conditions,\n    // an error message is generated.\n    //\n    // ```proto\n    // message MyUInt64 {\n    //   // value must be greater than 5 [uint64.gt]\n    //   uint64 value = 1 [(buf.validate.field).uint64.gt = 5];\n    //\n    //   // value must be greater than 5 and less than 10 [uint64.gt_lt]\n    //   uint64 other_value = 2 [(buf.validate.field).uint64 = { gt: 5, lt: 10 }];\n    //\n    //   // value must be greater than 10 or less than 5 [uint64.gt_lt_exclusive]\n    //   uint64 another_value = 3 [(buf.validate.field).uint64 = { gt: 10, lt: 5 }];\n    // }\n    // ```\n    uint64 gt = 4 [\n      (predefined).cel = {\n        id: \"uint64.gt\"\n        expression:\n            \"!has(rules.lt) && !has(rules.lte) && this <= rules.gt\"\n                \"? 'value must be greater than %s'.format([rules.gt]) : ''\"\n      },\n      (predefined).cel = {\n        id: \"uint64.gt_lt\"\n        expression:\n            \"has(rules.lt) && rules.lt >= rules.gt && (this >= rules.lt || this <= rules.gt)\"\n                \"? 'value must be greater than %s and less than %s'.format([rules.gt, rules.lt]) : ''\"\n      },\n      (predefined).cel = {\n        id: \"uint64.gt_lt_exclusive\"\n        expression:\n            \"has(rules.lt) && rules.lt < rules.gt && (rules.lt <= this && this <= rules.gt)\"\n                \"? 'value must be greater than %s or less than %s'.format([rules.gt, rules.lt]) : ''\"\n      },\n      (predefined).cel = {\n        id: \"uint64.gt_lte\"\n        expression:\n            \"has(rules.lte) && rules.lte >= rules.gt && (this > rules.lte || this <= rules.gt)\"\n                \"? 'value must be greater than %s and less than or equal to %s'.format([rules.gt, rules.lte]) : ''\"\n      },\n      (predefined).cel = {\n        id: \"uint64.gt_lte_exclusive\"\n        expression:\n            \"has(rules.lte) && rules.lte < rules.gt && (rules.lte < this && this <= rules.gt)\"\n                \"? 'value must be greater than %s or less than or equal to %s'.format([rules.gt, rules.lte]) : ''\"\n      }\n    ];\n\n    // `gte` requires the field value to be greater than or equal to the specified\n    // value (exclusive). If the value of `gte` is larger than a specified `lt`\n    // or `lte`, the range is reversed, and the field value must be outside the\n    // specified range. If the field value doesn't meet the required conditions,\n    // an error message is generated.\n    //\n    // ```proto\n    // message MyUInt64 {\n    //   // value must be greater than or equal to 5 [uint64.gte]\n    //   uint64 value = 1 [(buf.validate.field).uint64.gte = 5];\n    //\n    //   // value must be greater than or equal to 5 and less than 10 [uint64.gte_lt]\n    //   uint64 other_value = 2 [(buf.validate.field).uint64 = { gte: 5, lt: 10 }];\n    //\n    //   // value must be greater than or equal to 10 or less than 5 [uint64.gte_lt_exclusive]\n    //   uint64 another_value = 3 [(buf.validate.field).uint64 = { gte: 10, lt: 5 }];\n    // }\n    // ```\n    uint64 gte = 5 [\n      (predefined).cel = {\n        id: \"uint64.gte\"\n        expression:\n            \"!has(rules.lt) && !has(rules.lte) && this < rules.gte\"\n                \"? 'value must be greater than or equal to %s'.format([rules.gte]) : ''\"\n      },\n      (predefined).cel = {\n        id: \"uint64.gte_lt\"\n        expression:\n            \"has(rules.lt) && rules.lt >= rules.gte && (this >= rules.lt || this < rules.gte)\"\n                \"? 'value must be greater than or equal to %s and less than %s'.format([rules.gte, rules.lt]) : ''\"\n      },\n      (predefined).cel = {\n        id: \"uint64.gte_lt_exclusive\"\n        expression:\n            \"has(rules.lt) && rules.lt < rules.gte && (rules.lt <= this && this < rules.gte)\"\n                \"? 'value must be greater than or equal to %s or less than %s'.format([rules.gte, rules.lt]) : ''\"\n      },\n      (predefined).cel = {\n        id: \"uint64.gte_lte\"\n        expression:\n            \"has(rules.lte) && rules.lte >= rules.gte && (this > rules.lte || this < rules.gte)\"\n                \"? 'value must be greater than or equal to %s and less than or equal to %s'.format([rules.gte, rules.lte]) : ''\"\n      },\n      (predefined).cel = {\n        id: \"uint64.gte_lte_exclusive\"\n        expression:\n            \"has(rules.lte) && rules.lte < rules.gte && (rules.lte < this && this < rules.gte)\"\n                \"? 'value must be greater than or equal to %s or less than or equal to %s'.format([rules.gte, rules.lte]) : ''\"\n      }\n    ];\n  }\n  // `in` requires the field value to be equal to one of the specified values.\n  // If the field value isn't one of the specified values, an error message is\n  // generated.\n  //\n  // ```proto\n  // message MyUInt64 {\n  //   // value must be in list [1, 2, 3]\n  //   repeated uint64 value = 1 (buf.validate.field).uint64 = { in: [1, 2, 3] };\n  // }\n  // ```\n  repeated uint64 in = 6 [(predefined).cel = {\n    id: \"uint64.in\"\n    expression: \"!(this in dyn(rules)['in']) ? 'value must be in list %s'.format([dyn(rules)['in']]) : ''\"\n  }];\n\n  // `not_in` requires the field value to not be equal to any of the specified\n  // values. If the field value is one of the specified values, an error\n  // message is generated.\n  //\n  // ```proto\n  // message MyUInt64 {\n  //   // value must not be in list [1, 2, 3]\n  //   repeated uint64 value = 1 (buf.validate.field).uint64 = { not_in: [1, 2, 3] };\n  // }\n  // ```\n  repeated uint64 not_in = 7 [(predefined).cel = {\n    id: \"uint64.not_in\"\n    expression: \"this in rules.not_in ? 'value must not be in list %s'.format([rules.not_in]) : ''\"\n  }];\n\n  // `example` specifies values that the field may have. These values SHOULD\n  // conform to other constraints. `example` values will not impact validation\n  // but may be used as helpful guidance on how to populate the given field.\n  //\n  // ```proto\n  // message MyUInt64 {\n  //   uint64 value = 1 [\n  //     (buf.validate.field).uint64.example = 1,\n  //     (buf.validate.field).uint64.example = -10\n  //   ];\n  // }\n  // ```\n  repeated uint64 example = 8 [(predefined).cel = {\n    id: \"uint64.example\"\n    expression: \"true\"\n  }];\n\n  // Extension fields in this range that have the (buf.validate.predefined)\n  // option set will be treated as predefined field constraints that can then be\n  // set on the field options of other fields to apply field constraints.\n  // Extension numbers 1000 to 99999 are reserved for extension numbers that are\n  // defined in the [Protobuf Global Extension Registry][1]. Extension numbers\n  // above this range are reserved for extension numbers that are not explicitly\n  // assigned. For rules defined in publicly-consumed schemas, use of extensions\n  // above 99999 is discouraged due to the risk of conflicts.\n  //\n  // [1]: https://github.com/protocolbuffers/protobuf/blob/main/docs/options.md\n  extensions 1000 to max;\n}\n\n// SInt32Rules describes the constraints applied to `sint32` values.\nmessage SInt32Rules {\n  // `const` requires the field value to exactly match the specified value. If\n  // the field value doesn't match, an error message is generated.\n  //\n  // ```proto\n  // message MySInt32 {\n  //   // value must equal 42\n  //   sint32 value = 1 [(buf.validate.field).sint32.const = 42];\n  // }\n  // ```\n  optional sint32 const = 1 [(predefined).cel = {\n    id: \"sint32.const\"\n    expression: \"this != rules.const ? 'value must equal %s'.format([rules.const]) : ''\"\n  }];\n  oneof less_than {\n    // `lt` requires the field value to be less than the specified value (field\n    // < value). If the field value is equal to or greater than the specified\n    // value, an error message is generated.\n    //\n    // ```proto\n    // message MySInt32 {\n    //   // value must be less than 10\n    //   sint32 value = 1 [(buf.validate.field).sint32.lt = 10];\n    // }\n    // ```\n    sint32 lt = 2 [(predefined).cel = {\n      id: \"sint32.lt\"\n      expression:\n          \"!has(rules.gte) && !has(rules.gt) && this >= rules.lt\"\n              \"? 'value must be less than %s'.format([rules.lt]) : ''\"\n    }];\n\n    // `lte` requires the field value to be less than or equal to the specified\n    // value (field <= value). If the field value is greater than the specified\n    // value, an error message is generated.\n    //\n    // ```proto\n    // message MySInt32 {\n    //   // value must be less than or equal to 10\n    //   sint32 value = 1 [(buf.validate.field).sint32.lte = 10];\n    // }\n    // ```\n    sint32 lte = 3 [(predefined).cel = {\n      id: \"sint32.lte\"\n      expression:\n          \"!has(rules.gte) && !has(rules.gt) && this > rules.lte\"\n              \"? 'value must be less than or equal to %s'.format([rules.lte]) : ''\"\n    }];\n  }\n  oneof greater_than {\n    // `gt` requires the field value to be greater than the specified value\n    // (exclusive). If the value of `gt` is larger than a specified `lt` or\n    // `lte`, the range is reversed, and the field value must be outside the\n    // specified range. If the field value doesn't meet the required conditions,\n    // an error message is generated.\n    //\n    // ```proto\n    // message MySInt32 {\n    //   // value must be greater than 5 [sint32.gt]\n    //   sint32 value = 1 [(buf.validate.field).sint32.gt = 5];\n    //\n    //   // value must be greater than 5 and less than 10 [sint32.gt_lt]\n    //   sint32 other_value = 2 [(buf.validate.field).sint32 = { gt: 5, lt: 10 }];\n    //\n    //   // value must be greater than 10 or less than 5 [sint32.gt_lt_exclusive]\n    //   sint32 another_value = 3 [(buf.validate.field).sint32 = { gt: 10, lt: 5 }];\n    // }\n    // ```\n    sint32 gt = 4 [\n      (predefined).cel = {\n        id: \"sint32.gt\"\n        expression:\n            \"!has(rules.lt) && !has(rules.lte) && this <= rules.gt\"\n                \"? 'value must be greater than %s'.format([rules.gt]) : ''\"\n      },\n      (predefined).cel = {\n        id: \"sint32.gt_lt\"\n        expression:\n            \"has(rules.lt) && rules.lt >= rules.gt && (this >= rules.lt || this <= rules.gt)\"\n                \"? 'value must be greater than %s and less than %s'.format([rules.gt, rules.lt]) : ''\"\n      },\n      (predefined).cel = {\n        id: \"sint32.gt_lt_exclusive\"\n        expression:\n            \"has(rules.lt) && rules.lt < rules.gt && (rules.lt <= this && this <= rules.gt)\"\n                \"? 'value must be greater than %s or less than %s'.format([rules.gt, rules.lt]) : ''\"\n      },\n      (predefined).cel = {\n        id: \"sint32.gt_lte\"\n        expression:\n            \"has(rules.lte) && rules.lte >= rules.gt && (this > rules.lte || this <= rules.gt)\"\n                \"? 'value must be greater than %s and less than or equal to %s'.format([rules.gt, rules.lte]) : ''\"\n      },\n      (predefined).cel = {\n        id: \"sint32.gt_lte_exclusive\"\n        expression:\n            \"has(rules.lte) && rules.lte < rules.gt && (rules.lte < this && this <= rules.gt)\"\n                \"? 'value must be greater than %s or less than or equal to %s'.format([rules.gt, rules.lte]) : ''\"\n      }\n    ];\n\n    // `gte` requires the field value to be greater than or equal to the specified\n    // value (exclusive). If the value of `gte` is larger than a specified `lt`\n    // or `lte`, the range is reversed, and the field value must be outside the\n    // specified range. If the field value doesn't meet the required conditions,\n    // an error message is generated.\n    //\n    // ```proto\n    // message MySInt32 {\n    //  // value must be greater than or equal to 5 [sint32.gte]\n    //  sint32 value = 1 [(buf.validate.field).sint32.gte = 5];\n    //\n    //  // value must be greater than or equal to 5 and less than 10 [sint32.gte_lt]\n    //  sint32 other_value = 2 [(buf.validate.field).sint32 = { gte: 5, lt: 10 }];\n    //\n    //  // value must be greater than or equal to 10 or less than 5 [sint32.gte_lt_exclusive]\n    //  sint32 another_value = 3 [(buf.validate.field).sint32 = { gte: 10, lt: 5 }];\n    // }\n    // ```\n    sint32 gte = 5 [\n      (predefined).cel = {\n        id: \"sint32.gte\"\n        expression:\n            \"!has(rules.lt) && !has(rules.lte) && this < rules.gte\"\n                \"? 'value must be greater than or equal to %s'.format([rules.gte]) : ''\"\n      },\n      (predefined).cel = {\n        id: \"sint32.gte_lt\"\n        expression:\n            \"has(rules.lt) && rules.lt >= rules.gte && (this >= rules.lt || this < rules.gte)\"\n                \"? 'value must be greater than or equal to %s and less than %s'.format([rules.gte, rules.lt]) : ''\"\n      },\n      (predefined).cel = {\n        id: \"sint32.gte_lt_exclusive\"\n        expression:\n            \"has(rules.lt) && rules.lt < rules.gte && (rules.lt <= this && this < rules.gte)\"\n                \"? 'value must be greater than or equal to %s or less than %s'.format([rules.gte, rules.lt]) : ''\"\n      },\n      (predefined).cel = {\n        id: \"sint32.gte_lte\"\n        expression:\n            \"has(rules.lte) && rules.lte >= rules.gte && (this > rules.lte || this < rules.gte)\"\n                \"? 'value must be greater than or equal to %s and less than or equal to %s'.format([rules.gte, rules.lte]) : ''\"\n      },\n      (predefined).cel = {\n        id: \"sint32.gte_lte_exclusive\"\n        expression:\n            \"has(rules.lte) && rules.lte < rules.gte && (rules.lte < this && this < rules.gte)\"\n                \"? 'value must be greater than or equal to %s or less than or equal to %s'.format([rules.gte, rules.lte]) : ''\"\n      }\n    ];\n  }\n\n  // `in` requires the field value to be equal to one of the specified values.\n  // If the field value isn't one of the specified values, an error message is\n  // generated.\n  //\n  // ```proto\n  // message MySInt32 {\n  //   // value must be in list [1, 2, 3]\n  //   repeated sint32 value = 1 (buf.validate.field).sint32 = { in: [1, 2, 3] };\n  // }\n  // ```\n  repeated sint32 in = 6 [(predefined).cel = {\n    id: \"sint32.in\"\n    expression: \"!(this in dyn(rules)['in']) ? 'value must be in list %s'.format([dyn(rules)['in']]) : ''\"\n  }];\n\n  // `not_in` requires the field value to not be equal to any of the specified\n  // values. If the field value is one of the specified values, an error\n  // message is generated.\n  //\n  // ```proto\n  // message MySInt32 {\n  //   // value must not be in list [1, 2, 3]\n  //   repeated sint32 value = 1 (buf.validate.field).sint32 = { not_in: [1, 2, 3] };\n  // }\n  // ```\n  repeated sint32 not_in = 7 [(predefined).cel = {\n    id: \"sint32.not_in\"\n    expression: \"this in rules.not_in ? 'value must not be in list %s'.format([rules.not_in]) : ''\"\n  }];\n\n  // `example` specifies values that the field may have. These values SHOULD\n  // conform to other constraints. `example` values will not impact validation\n  // but may be used as helpful guidance on how to populate the given field.\n  //\n  // ```proto\n  // message MySInt32 {\n  //   sint32 value = 1 [\n  //     (buf.validate.field).sint32.example = 1,\n  //     (buf.validate.field).sint32.example = -10\n  //   ];\n  // }\n  // ```\n  repeated sint32 example = 8 [(predefined).cel = {\n    id: \"sint32.example\"\n    expression: \"true\"\n  }];\n\n  // Extension fields in this range that have the (buf.validate.predefined)\n  // option set will be treated as predefined field constraints that can then be\n  // set on the field options of other fields to apply field constraints.\n  // Extension numbers 1000 to 99999 are reserved for extension numbers that are\n  // defined in the [Protobuf Global Extension Registry][1]. Extension numbers\n  // above this range are reserved for extension numbers that are not explicitly\n  // assigned. For rules defined in publicly-consumed schemas, use of extensions\n  // above 99999 is discouraged due to the risk of conflicts.\n  //\n  // [1]: https://github.com/protocolbuffers/protobuf/blob/main/docs/options.md\n  extensions 1000 to max;\n}\n\n// SInt64Rules describes the constraints applied to `sint64` values.\nmessage SInt64Rules {\n  // `const` requires the field value to exactly match the specified value. If\n  // the field value doesn't match, an error message is generated.\n  //\n  // ```proto\n  // message MySInt64 {\n  //   // value must equal 42\n  //   sint64 value = 1 [(buf.validate.field).sint64.const = 42];\n  // }\n  // ```\n  optional sint64 const = 1 [(predefined).cel = {\n    id: \"sint64.const\"\n    expression: \"this != rules.const ? 'value must equal %s'.format([rules.const]) : ''\"\n  }];\n  oneof less_than {\n    // `lt` requires the field value to be less than the specified value (field\n    // < value). If the field value is equal to or greater than the specified\n    // value, an error message is generated.\n    //\n    // ```proto\n    // message MySInt64 {\n    //   // value must be less than 10\n    //   sint64 value = 1 [(buf.validate.field).sint64.lt = 10];\n    // }\n    // ```\n    sint64 lt = 2 [(predefined).cel = {\n      id: \"sint64.lt\"\n      expression:\n          \"!has(rules.gte) && !has(rules.gt) && this >= rules.lt\"\n              \"? 'value must be less than %s'.format([rules.lt]) : ''\"\n    }];\n\n    // `lte` requires the field value to be less than or equal to the specified\n    // value (field <= value). If the field value is greater than the specified\n    // value, an error message is generated.\n    //\n    // ```proto\n    // message MySInt64 {\n    //   // value must be less than or equal to 10\n    //   sint64 value = 1 [(buf.validate.field).sint64.lte = 10];\n    // }\n    // ```\n    sint64 lte = 3 [(predefined).cel = {\n      id: \"sint64.lte\"\n      expression:\n          \"!has(rules.gte) && !has(rules.gt) && this > rules.lte\"\n              \"? 'value must be less than or equal to %s'.format([rules.lte]) : ''\"\n    }];\n  }\n  oneof greater_than {\n    // `gt` requires the field value to be greater than the specified value\n    // (exclusive). If the value of `gt` is larger than a specified `lt` or\n    // `lte`, the range is reversed, and the field value must be outside the\n    // specified range. If the field value doesn't meet the required conditions,\n    // an error message is generated.\n    //\n    // ```proto\n    // message MySInt64 {\n    //   // value must be greater than 5 [sint64.gt]\n    //   sint64 value = 1 [(buf.validate.field).sint64.gt = 5];\n    //\n    //   // value must be greater than 5 and less than 10 [sint64.gt_lt]\n    //   sint64 other_value = 2 [(buf.validate.field).sint64 = { gt: 5, lt: 10 }];\n    //\n    //   // value must be greater than 10 or less than 5 [sint64.gt_lt_exclusive]\n    //   sint64 another_value = 3 [(buf.validate.field).sint64 = { gt: 10, lt: 5 }];\n    // }\n    // ```\n    sint64 gt = 4 [\n      (predefined).cel = {\n        id: \"sint64.gt\"\n        expression:\n            \"!has(rules.lt) && !has(rules.lte) && this <= rules.gt\"\n                \"? 'value must be greater than %s'.format([rules.gt]) : ''\"\n      },\n      (predefined).cel = {\n        id: \"sint64.gt_lt\"\n        expression:\n            \"has(rules.lt) && rules.lt >= rules.gt && (this >= rules.lt || this <= rules.gt)\"\n                \"? 'value must be greater than %s and less than %s'.format([rules.gt, rules.lt]) : ''\"\n      },\n      (predefined).cel = {\n        id: \"sint64.gt_lt_exclusive\"\n        expression:\n            \"has(rules.lt) && rules.lt < rules.gt && (rules.lt <= this && this <= rules.gt)\"\n                \"? 'value must be greater than %s or less than %s'.format([rules.gt, rules.lt]) : ''\"\n      },\n      (predefined).cel = {\n        id: \"sint64.gt_lte\"\n        expression:\n            \"has(rules.lte) && rules.lte >= rules.gt && (this > rules.lte || this <= rules.gt)\"\n                \"? 'value must be greater than %s and less than or equal to %s'.format([rules.gt, rules.lte]) : ''\"\n      },\n      (predefined).cel = {\n        id: \"sint64.gt_lte_exclusive\"\n        expression:\n            \"has(rules.lte) && rules.lte < rules.gt && (rules.lte < this && this <= rules.gt)\"\n                \"? 'value must be greater than %s or less than or equal to %s'.format([rules.gt, rules.lte]) : ''\"\n      }\n    ];\n\n    // `gte` requires the field value to be greater than or equal to the specified\n    // value (exclusive). If the value of `gte` is larger than a specified `lt`\n    // or `lte`, the range is reversed, and the field value must be outside the\n    // specified range. If the field value doesn't meet the required conditions,\n    // an error message is generated.\n    //\n    // ```proto\n    // message MySInt64 {\n    //   // value must be greater than or equal to 5 [sint64.gte]\n    //   sint64 value = 1 [(buf.validate.field).sint64.gte = 5];\n    //\n    //   // value must be greater than or equal to 5 and less than 10 [sint64.gte_lt]\n    //   sint64 other_value = 2 [(buf.validate.field).sint64 = { gte: 5, lt: 10 }];\n    //\n    //   // value must be greater than or equal to 10 or less than 5 [sint64.gte_lt_exclusive]\n    //   sint64 another_value = 3 [(buf.validate.field).sint64 = { gte: 10, lt: 5 }];\n    // }\n    // ```\n    sint64 gte = 5 [\n      (predefined).cel = {\n        id: \"sint64.gte\"\n        expression:\n            \"!has(rules.lt) && !has(rules.lte) && this < rules.gte\"\n                \"? 'value must be greater than or equal to %s'.format([rules.gte]) : ''\"\n      },\n      (predefined).cel = {\n        id: \"sint64.gte_lt\"\n        expression:\n            \"has(rules.lt) && rules.lt >= rules.gte && (this >= rules.lt || this < rules.gte)\"\n                \"? 'value must be greater than or equal to %s and less than %s'.format([rules.gte, rules.lt]) : ''\"\n      },\n      (predefined).cel = {\n        id: \"sint64.gte_lt_exclusive\"\n        expression:\n            \"has(rules.lt) && rules.lt < rules.gte && (rules.lt <= this && this < rules.gte)\"\n                \"? 'value must be greater than or equal to %s or less than %s'.format([rules.gte, rules.lt]) : ''\"\n      },\n      (predefined).cel = {\n        id: \"sint64.gte_lte\"\n        expression:\n            \"has(rules.lte) && rules.lte >= rules.gte && (this > rules.lte || this < rules.gte)\"\n                \"? 'value must be greater than or equal to %s and less than or equal to %s'.format([rules.gte, rules.lte]) : ''\"\n      },\n      (predefined).cel = {\n        id: \"sint64.gte_lte_exclusive\"\n        expression:\n            \"has(rules.lte) && rules.lte < rules.gte && (rules.lte < this && this < rules.gte)\"\n                \"? 'value must be greater than or equal to %s or less than or equal to %s'.format([rules.gte, rules.lte]) : ''\"\n      }\n    ];\n  }\n\n  // `in` requires the field value to be equal to one of the specified values.\n  // If the field value isn't one of the specified values, an error message\n  // is generated.\n  //\n  // ```proto\n  // message MySInt64 {\n  //   // value must be in list [1, 2, 3]\n  //   repeated sint64 value = 1 (buf.validate.field).sint64 = { in: [1, 2, 3] };\n  // }\n  // ```\n  repeated sint64 in = 6 [(predefined).cel = {\n    id: \"sint64.in\"\n    expression: \"!(this in dyn(rules)['in']) ? 'value must be in list %s'.format([dyn(rules)['in']]) : ''\"\n  }];\n\n  // `not_in` requires the field value to not be equal to any of the specified\n  // values. If the field value is one of the specified values, an error\n  // message is generated.\n  //\n  // ```proto\n  // message MySInt64 {\n  //   // value must not be in list [1, 2, 3]\n  //   repeated sint64 value = 1 (buf.validate.field).sint64 = { not_in: [1, 2, 3] };\n  // }\n  // ```\n  repeated sint64 not_in = 7 [(predefined).cel = {\n    id: \"sint64.not_in\"\n    expression: \"this in rules.not_in ? 'value must not be in list %s'.format([rules.not_in]) : ''\"\n  }];\n\n  // `example` specifies values that the field may have. These values SHOULD\n  // conform to other constraints. `example` values will not impact validation\n  // but may be used as helpful guidance on how to populate the given field.\n  //\n  // ```proto\n  // message MySInt64 {\n  //   sint64 value = 1 [\n  //     (buf.validate.field).sint64.example = 1,\n  //     (buf.validate.field).sint64.example = -10\n  //   ];\n  // }\n  // ```\n  repeated sint64 example = 8 [(predefined).cel = {\n    id: \"sint64.example\"\n    expression: \"true\"\n  }];\n\n  // Extension fields in this range that have the (buf.validate.predefined)\n  // option set will be treated as predefined field constraints that can then be\n  // set on the field options of other fields to apply field constraints.\n  // Extension numbers 1000 to 99999 are reserved for extension numbers that are\n  // defined in the [Protobuf Global Extension Registry][1]. Extension numbers\n  // above this range are reserved for extension numbers that are not explicitly\n  // assigned. For rules defined in publicly-consumed schemas, use of extensions\n  // above 99999 is discouraged due to the risk of conflicts.\n  //\n  // [1]: https://github.com/protocolbuffers/protobuf/blob/main/docs/options.md\n  extensions 1000 to max;\n}\n\n// Fixed32Rules describes the constraints applied to `fixed32` values.\nmessage Fixed32Rules {\n  // `const` requires the field value to exactly match the specified value.\n  // If the field value doesn't match, an error message is generated.\n  //\n  // ```proto\n  // message MyFixed32 {\n  //   // value must equal 42\n  //   fixed32 value = 1 [(buf.validate.field).fixed32.const = 42];\n  // }\n  // ```\n  optional fixed32 const = 1 [(predefined).cel = {\n    id: \"fixed32.const\"\n    expression: \"this != rules.const ? 'value must equal %s'.format([rules.const]) : ''\"\n  }];\n  oneof less_than {\n    // `lt` requires the field value to be less than the specified value (field <\n    // value). If the field value is equal to or greater than the specified value,\n    // an error message is generated.\n    //\n    // ```proto\n    // message MyFixed32 {\n    //   // value must be less than 10\n    //   fixed32 value = 1 [(buf.validate.field).fixed32.lt = 10];\n    // }\n    // ```\n    fixed32 lt = 2 [(predefined).cel = {\n      id: \"fixed32.lt\"\n      expression:\n          \"!has(rules.gte) && !has(rules.gt) && this >= rules.lt\"\n              \"? 'value must be less than %s'.format([rules.lt]) : ''\"\n    }];\n\n    // `lte` requires the field value to be less than or equal to the specified\n    // value (field <= value). If the field value is greater than the specified\n    // value, an error message is generated.\n    //\n    // ```proto\n    // message MyFixed32 {\n    //   // value must be less than or equal to 10\n    //   fixed32 value = 1 [(buf.validate.field).fixed32.lte = 10];\n    // }\n    // ```\n    fixed32 lte = 3 [(predefined).cel = {\n      id: \"fixed32.lte\"\n      expression:\n          \"!has(rules.gte) && !has(rules.gt) && this > rules.lte\"\n              \"? 'value must be less than or equal to %s'.format([rules.lte]) : ''\"\n    }];\n  }\n  oneof greater_than {\n    // `gt` requires the field value to be greater than the specified value\n    // (exclusive). If the value of `gt` is larger than a specified `lt` or\n    // `lte`, the range is reversed, and the field value must be outside the\n    // specified range. If the field value doesn't meet the required conditions,\n    // an error message is generated.\n    //\n    // ```proto\n    // message MyFixed32 {\n    //   // value must be greater than 5 [fixed32.gt]\n    //   fixed32 value = 1 [(buf.validate.field).fixed32.gt = 5];\n    //\n    //   // value must be greater than 5 and less than 10 [fixed32.gt_lt]\n    //   fixed32 other_value = 2 [(buf.validate.field).fixed32 = { gt: 5, lt: 10 }];\n    //\n    //   // value must be greater than 10 or less than 5 [fixed32.gt_lt_exclusive]\n    //   fixed32 another_value = 3 [(buf.validate.field).fixed32 = { gt: 10, lt: 5 }];\n    // }\n    // ```\n    fixed32 gt = 4 [\n      (predefined).cel = {\n        id: \"fixed32.gt\"\n        expression:\n            \"!has(rules.lt) && !has(rules.lte) && this <= rules.gt\"\n                \"? 'value must be greater than %s'.format([rules.gt]) : ''\"\n      },\n      (predefined).cel = {\n        id: \"fixed32.gt_lt\"\n        expression:\n            \"has(rules.lt) && rules.lt >= rules.gt && (this >= rules.lt || this <= rules.gt)\"\n                \"? 'value must be greater than %s and less than %s'.format([rules.gt, rules.lt]) : ''\"\n      },\n      (predefined).cel = {\n        id: \"fixed32.gt_lt_exclusive\"\n        expression:\n            \"has(rules.lt) && rules.lt < rules.gt && (rules.lt <= this && this <= rules.gt)\"\n                \"? 'value must be greater than %s or less than %s'.format([rules.gt, rules.lt]) : ''\"\n      },\n      (predefined).cel = {\n        id: \"fixed32.gt_lte\"\n        expression:\n            \"has(rules.lte) && rules.lte >= rules.gt && (this > rules.lte || this <= rules.gt)\"\n                \"? 'value must be greater than %s and less than or equal to %s'.format([rules.gt, rules.lte]) : ''\"\n      },\n      (predefined).cel = {\n        id: \"fixed32.gt_lte_exclusive\"\n        expression:\n            \"has(rules.lte) && rules.lte < rules.gt && (rules.lte < this && this <= rules.gt)\"\n                \"? 'value must be greater than %s or less than or equal to %s'.format([rules.gt, rules.lte]) : ''\"\n      }\n    ];\n\n    // `gte` requires the field value to be greater than or equal to the specified\n    // value (exclusive). If the value of `gte` is larger than a specified `lt`\n    // or `lte`, the range is reversed, and the field value must be outside the\n    // specified range. If the field value doesn't meet the required conditions,\n    // an error message is generated.\n    //\n    // ```proto\n    // message MyFixed32 {\n    //   // value must be greater than or equal to 5 [fixed32.gte]\n    //   fixed32 value = 1 [(buf.validate.field).fixed32.gte = 5];\n    //\n    //   // value must be greater than or equal to 5 and less than 10 [fixed32.gte_lt]\n    //   fixed32 other_value = 2 [(buf.validate.field).fixed32 = { gte: 5, lt: 10 }];\n    //\n    //   // value must be greater than or equal to 10 or less than 5 [fixed32.gte_lt_exclusive]\n    //   fixed32 another_value = 3 [(buf.validate.field).fixed32 = { gte: 10, lt: 5 }];\n    // }\n    // ```\n    fixed32 gte = 5 [\n      (predefined).cel = {\n        id: \"fixed32.gte\"\n        expression:\n            \"!has(rules.lt) && !has(rules.lte) && this < rules.gte\"\n                \"? 'value must be greater than or equal to %s'.format([rules.gte]) : ''\"\n      },\n      (predefined).cel = {\n        id: \"fixed32.gte_lt\"\n        expression:\n            \"has(rules.lt) && rules.lt >= rules.gte && (this >= rules.lt || this < rules.gte)\"\n                \"? 'value must be greater than or equal to %s and less than %s'.format([rules.gte, rules.lt]) : ''\"\n      },\n      (predefined).cel = {\n        id: \"fixed32.gte_lt_exclusive\"\n        expression:\n            \"has(rules.lt) && rules.lt < rules.gte && (rules.lt <= this && this < rules.gte)\"\n                \"? 'value must be greater than or equal to %s or less than %s'.format([rules.gte, rules.lt]) : ''\"\n      },\n      (predefined).cel = {\n        id: \"fixed32.gte_lte\"\n        expression:\n            \"has(rules.lte) && rules.lte >= rules.gte && (this > rules.lte || this < rules.gte)\"\n                \"? 'value must be greater than or equal to %s and less than or equal to %s'.format([rules.gte, rules.lte]) : ''\"\n      },\n      (predefined).cel = {\n        id: \"fixed32.gte_lte_exclusive\"\n        expression:\n            \"has(rules.lte) && rules.lte < rules.gte && (rules.lte < this && this < rules.gte)\"\n                \"? 'value must be greater than or equal to %s or less than or equal to %s'.format([rules.gte, rules.lte]) : ''\"\n      }\n    ];\n  }\n\n  // `in` requires the field value to be equal to one of the specified values.\n  // If the field value isn't one of the specified values, an error message\n  // is generated.\n  //\n  // ```proto\n  // message MyFixed32 {\n  //   // value must be in list [1, 2, 3]\n  //   repeated fixed32 value = 1 (buf.validate.field).fixed32 = { in: [1, 2, 3] };\n  // }\n  // ```\n  repeated fixed32 in = 6 [(predefined).cel = {\n    id: \"fixed32.in\"\n    expression: \"!(this in dyn(rules)['in']) ? 'value must be in list %s'.format([dyn(rules)['in']]) : ''\"\n  }];\n\n  // `not_in` requires the field value to not be equal to any of the specified\n  // values. If the field value is one of the specified values, an error\n  // message is generated.\n  //\n  // ```proto\n  // message MyFixed32 {\n  //   // value must not be in list [1, 2, 3]\n  //   repeated fixed32 value = 1 (buf.validate.field).fixed32 = { not_in: [1, 2, 3] };\n  // }\n  // ```\n  repeated fixed32 not_in = 7 [(predefined).cel = {\n    id: \"fixed32.not_in\"\n    expression: \"this in rules.not_in ? 'value must not be in list %s'.format([rules.not_in]) : ''\"\n  }];\n\n  // `example` specifies values that the field may have. These values SHOULD\n  // conform to other constraints. `example` values will not impact validation\n  // but may be used as helpful guidance on how to populate the given field.\n  //\n  // ```proto\n  // message MyFixed32 {\n  //   fixed32 value = 1 [\n  //     (buf.validate.field).fixed32.example = 1,\n  //     (buf.validate.field).fixed32.example = 2\n  //   ];\n  // }\n  // ```\n  repeated fixed32 example = 8 [(predefined).cel = {\n    id: \"fixed32.example\"\n    expression: \"true\"\n  }];\n\n  // Extension fields in this range that have the (buf.validate.predefined)\n  // option set will be treated as predefined field constraints that can then be\n  // set on the field options of other fields to apply field constraints.\n  // Extension numbers 1000 to 99999 are reserved for extension numbers that are\n  // defined in the [Protobuf Global Extension Registry][1]. Extension numbers\n  // above this range are reserved for extension numbers that are not explicitly\n  // assigned. For rules defined in publicly-consumed schemas, use of extensions\n  // above 99999 is discouraged due to the risk of conflicts.\n  //\n  // [1]: https://github.com/protocolbuffers/protobuf/blob/main/docs/options.md\n  extensions 1000 to max;\n}\n\n// Fixed64Rules describes the constraints applied to `fixed64` values.\nmessage Fixed64Rules {\n  // `const` requires the field value to exactly match the specified value. If\n  // the field value doesn't match, an error message is generated.\n  //\n  // ```proto\n  // message MyFixed64 {\n  //   // value must equal 42\n  //   fixed64 value = 1 [(buf.validate.field).fixed64.const = 42];\n  // }\n  // ```\n  optional fixed64 const = 1 [(predefined).cel = {\n    id: \"fixed64.const\"\n    expression: \"this != rules.const ? 'value must equal %s'.format([rules.const]) : ''\"\n  }];\n  oneof less_than {\n    // `lt` requires the field value to be less than the specified value (field <\n    // value). If the field value is equal to or greater than the specified value,\n    // an error message is generated.\n    //\n    // ```proto\n    // message MyFixed64 {\n    //   // value must be less than 10\n    //   fixed64 value = 1 [(buf.validate.field).fixed64.lt = 10];\n    // }\n    // ```\n    fixed64 lt = 2 [(predefined).cel = {\n      id: \"fixed64.lt\"\n      expression:\n          \"!has(rules.gte) && !has(rules.gt) && this >= rules.lt\"\n              \"? 'value must be less than %s'.format([rules.lt]) : ''\"\n    }];\n\n    // `lte` requires the field value to be less than or equal to the specified\n    // value (field <= value). If the field value is greater than the specified\n    // value, an error message is generated.\n    //\n    // ```proto\n    // message MyFixed64 {\n    //   // value must be less than or equal to 10\n    //   fixed64 value = 1 [(buf.validate.field).fixed64.lte = 10];\n    // }\n    // ```\n    fixed64 lte = 3 [(predefined).cel = {\n      id: \"fixed64.lte\"\n      expression:\n          \"!has(rules.gte) && !has(rules.gt) && this > rules.lte\"\n              \"? 'value must be less than or equal to %s'.format([rules.lte]) : ''\"\n    }];\n  }\n  oneof greater_than {\n    // `gt` requires the field value to be greater than the specified value\n    // (exclusive). If the value of `gt` is larger than a specified `lt` or\n    // `lte`, the range is reversed, and the field value must be outside the\n    // specified range. If the field value doesn't meet the required conditions,\n    // an error message is generated.\n    //\n    // ```proto\n    // message MyFixed64 {\n    //   // value must be greater than 5 [fixed64.gt]\n    //   fixed64 value = 1 [(buf.validate.field).fixed64.gt = 5];\n    //\n    //   // value must be greater than 5 and less than 10 [fixed64.gt_lt]\n    //   fixed64 other_value = 2 [(buf.validate.field).fixed64 = { gt: 5, lt: 10 }];\n    //\n    //   // value must be greater than 10 or less than 5 [fixed64.gt_lt_exclusive]\n    //   fixed64 another_value = 3 [(buf.validate.field).fixed64 = { gt: 10, lt: 5 }];\n    // }\n    // ```\n    fixed64 gt = 4 [\n      (predefined).cel = {\n        id: \"fixed64.gt\"\n        expression:\n            \"!has(rules.lt) && !has(rules.lte) && this <= rules.gt\"\n                \"? 'value must be greater than %s'.format([rules.gt]) : ''\"\n      },\n      (predefined).cel = {\n        id: \"fixed64.gt_lt\"\n        expression:\n            \"has(rules.lt) && rules.lt >= rules.gt && (this >= rules.lt || this <= rules.gt)\"\n                \"? 'value must be greater than %s and less than %s'.format([rules.gt, rules.lt]) : ''\"\n      },\n      (predefined).cel = {\n        id: \"fixed64.gt_lt_exclusive\"\n        expression:\n            \"has(rules.lt) && rules.lt < rules.gt && (rules.lt <= this && this <= rules.gt)\"\n                \"? 'value must be greater than %s or less than %s'.format([rules.gt, rules.lt]) : ''\"\n      },\n      (predefined).cel = {\n        id: \"fixed64.gt_lte\"\n        expression:\n            \"has(rules.lte) && rules.lte >= rules.gt && (this > rules.lte || this <= rules.gt)\"\n                \"? 'value must be greater than %s and less than or equal to %s'.format([rules.gt, rules.lte]) : ''\"\n      },\n      (predefined).cel = {\n        id: \"fixed64.gt_lte_exclusive\"\n        expression:\n            \"has(rules.lte) && rules.lte < rules.gt && (rules.lte < this && this <= rules.gt)\"\n                \"? 'value must be greater than %s or less than or equal to %s'.format([rules.gt, rules.lte]) : ''\"\n      }\n    ];\n\n    // `gte` requires the field value to be greater than or equal to the specified\n    // value (exclusive). If the value of `gte` is larger than a specified `lt`\n    // or `lte`, the range is reversed, and the field value must be outside the\n    // specified range. If the field value doesn't meet the required conditions,\n    // an error message is generated.\n    //\n    // ```proto\n    // message MyFixed64 {\n    //   // value must be greater than or equal to 5 [fixed64.gte]\n    //   fixed64 value = 1 [(buf.validate.field).fixed64.gte = 5];\n    //\n    //   // value must be greater than or equal to 5 and less than 10 [fixed64.gte_lt]\n    //   fixed64 other_value = 2 [(buf.validate.field).fixed64 = { gte: 5, lt: 10 }];\n    //\n    //   // value must be greater than or equal to 10 or less than 5 [fixed64.gte_lt_exclusive]\n    //   fixed64 another_value = 3 [(buf.validate.field).fixed64 = { gte: 10, lt: 5 }];\n    // }\n    // ```\n    fixed64 gte = 5 [\n      (predefined).cel = {\n        id: \"fixed64.gte\"\n        expression:\n            \"!has(rules.lt) && !has(rules.lte) && this < rules.gte\"\n                \"? 'value must be greater than or equal to %s'.format([rules.gte]) : ''\"\n      },\n      (predefined).cel = {\n        id: \"fixed64.gte_lt\"\n        expression:\n            \"has(rules.lt) && rules.lt >= rules.gte && (this >= rules.lt || this < rules.gte)\"\n                \"? 'value must be greater than or equal to %s and less than %s'.format([rules.gte, rules.lt]) : ''\"\n      },\n      (predefined).cel = {\n        id: \"fixed64.gte_lt_exclusive\"\n        expression:\n            \"has(rules.lt) && rules.lt < rules.gte && (rules.lt <= this && this < rules.gte)\"\n                \"? 'value must be greater than or equal to %s or less than %s'.format([rules.gte, rules.lt]) : ''\"\n      },\n      (predefined).cel = {\n        id: \"fixed64.gte_lte\"\n        expression:\n            \"has(rules.lte) && rules.lte >= rules.gte && (this > rules.lte || this < rules.gte)\"\n                \"? 'value must be greater than or equal to %s and less than or equal to %s'.format([rules.gte, rules.lte]) : ''\"\n      },\n      (predefined).cel = {\n        id: \"fixed64.gte_lte_exclusive\"\n        expression:\n            \"has(rules.lte) && rules.lte < rules.gte && (rules.lte < this && this < rules.gte)\"\n                \"? 'value must be greater than or equal to %s or less than or equal to %s'.format([rules.gte, rules.lte]) : ''\"\n      }\n    ];\n  }\n\n  // `in` requires the field value to be equal to one of the specified values.\n  // If the field value isn't one of the specified values, an error message is\n  // generated.\n  //\n  // ```proto\n  // message MyFixed64 {\n  //   // value must be in list [1, 2, 3]\n  //   repeated fixed64 value = 1 (buf.validate.field).fixed64 = { in: [1, 2, 3] };\n  // }\n  // ```\n  repeated fixed64 in = 6 [(predefined).cel = {\n    id: \"fixed64.in\"\n    expression: \"!(this in dyn(rules)['in']) ? 'value must be in list %s'.format([dyn(rules)['in']]) : ''\"\n  }];\n\n  // `not_in` requires the field value to not be equal to any of the specified\n  // values. If the field value is one of the specified values, an error\n  // message is generated.\n  //\n  // ```proto\n  // message MyFixed64 {\n  //   // value must not be in list [1, 2, 3]\n  //   repeated fixed64 value = 1 (buf.validate.field).fixed64 = { not_in: [1, 2, 3] };\n  // }\n  // ```\n  repeated fixed64 not_in = 7 [(predefined).cel = {\n    id: \"fixed64.not_in\"\n    expression: \"this in rules.not_in ? 'value must not be in list %s'.format([rules.not_in]) : ''\"\n  }];\n\n  // `example` specifies values that the field may have. These values SHOULD\n  // conform to other constraints. `example` values will not impact validation\n  // but may be used as helpful guidance on how to populate the given field.\n  //\n  // ```proto\n  // message MyFixed64 {\n  //   fixed64 value = 1 [\n  //     (buf.validate.field).fixed64.example = 1,\n  //     (buf.validate.field).fixed64.example = 2\n  //   ];\n  // }\n  // ```\n  repeated fixed64 example = 8 [(predefined).cel = {\n    id: \"fixed64.example\"\n    expression: \"true\"\n  }];\n\n  // Extension fields in this range that have the (buf.validate.predefined)\n  // option set will be treated as predefined field constraints that can then be\n  // set on the field options of other fields to apply field constraints.\n  // Extension numbers 1000 to 99999 are reserved for extension numbers that are\n  // defined in the [Protobuf Global Extension Registry][1]. Extension numbers\n  // above this range are reserved for extension numbers that are not explicitly\n  // assigned. For rules defined in publicly-consumed schemas, use of extensions\n  // above 99999 is discouraged due to the risk of conflicts.\n  //\n  // [1]: https://github.com/protocolbuffers/protobuf/blob/main/docs/options.md\n  extensions 1000 to max;\n}\n\n// SFixed32Rules describes the constraints applied to `fixed32` values.\nmessage SFixed32Rules {\n  // `const` requires the field value to exactly match the specified value. If\n  // the field value doesn't match, an error message is generated.\n  //\n  // ```proto\n  // message MySFixed32 {\n  //   // value must equal 42\n  //   sfixed32 value = 1 [(buf.validate.field).sfixed32.const = 42];\n  // }\n  // ```\n  optional sfixed32 const = 1 [(predefined).cel = {\n    id: \"sfixed32.const\"\n    expression: \"this != rules.const ? 'value must equal %s'.format([rules.const]) : ''\"\n  }];\n  oneof less_than {\n    // `lt` requires the field value to be less than the specified value (field <\n    // value). If the field value is equal to or greater than the specified value,\n    // an error message is generated.\n    //\n    // ```proto\n    // message MySFixed32 {\n    //   // value must be less than 10\n    //   sfixed32 value = 1 [(buf.validate.field).sfixed32.lt = 10];\n    // }\n    // ```\n    sfixed32 lt = 2 [(predefined).cel = {\n      id: \"sfixed32.lt\"\n      expression:\n          \"!has(rules.gte) && !has(rules.gt) && this >= rules.lt\"\n              \"? 'value must be less than %s'.format([rules.lt]) : ''\"\n    }];\n\n    // `lte` requires the field value to be less than or equal to the specified\n    // value (field <= value). If the field value is greater than the specified\n    // value, an error message is generated.\n    //\n    // ```proto\n    // message MySFixed32 {\n    //   // value must be less than or equal to 10\n    //   sfixed32 value = 1 [(buf.validate.field).sfixed32.lte = 10];\n    // }\n    // ```\n    sfixed32 lte = 3 [(predefined).cel = {\n      id: \"sfixed32.lte\"\n      expression:\n          \"!has(rules.gte) && !has(rules.gt) && this > rules.lte\"\n              \"? 'value must be less than or equal to %s'.format([rules.lte]) : ''\"\n    }];\n  }\n  oneof greater_than {\n    // `gt` requires the field value to be greater than the specified value\n    // (exclusive). If the value of `gt` is larger than a specified `lt` or\n    // `lte`, the range is reversed, and the field value must be outside the\n    // specified range. If the field value doesn't meet the required conditions,\n    // an error message is generated.\n    //\n    // ```proto\n    // message MySFixed32 {\n    //   // value must be greater than 5 [sfixed32.gt]\n    //   sfixed32 value = 1 [(buf.validate.field).sfixed32.gt = 5];\n    //\n    //   // value must be greater than 5 and less than 10 [sfixed32.gt_lt]\n    //   sfixed32 other_value = 2 [(buf.validate.field).sfixed32 = { gt: 5, lt: 10 }];\n    //\n    //   // value must be greater than 10 or less than 5 [sfixed32.gt_lt_exclusive]\n    //   sfixed32 another_value = 3 [(buf.validate.field).sfixed32 = { gt: 10, lt: 5 }];\n    // }\n    // ```\n    sfixed32 gt = 4 [\n      (predefined).cel = {\n        id: \"sfixed32.gt\"\n        expression:\n            \"!has(rules.lt) && !has(rules.lte) && this <= rules.gt\"\n                \"? 'value must be greater than %s'.format([rules.gt]) : ''\"\n      },\n      (predefined).cel = {\n        id: \"sfixed32.gt_lt\"\n        expression:\n            \"has(rules.lt) && rules.lt >= rules.gt && (this >= rules.lt || this <= rules.gt)\"\n                \"? 'value must be greater than %s and less than %s'.format([rules.gt, rules.lt]) : ''\"\n      },\n      (predefined).cel = {\n        id: \"sfixed32.gt_lt_exclusive\"\n        expression:\n            \"has(rules.lt) && rules.lt < rules.gt && (rules.lt <= this && this <= rules.gt)\"\n                \"? 'value must be greater than %s or less than %s'.format([rules.gt, rules.lt]) : ''\"\n      },\n      (predefined).cel = {\n        id: \"sfixed32.gt_lte\"\n        expression:\n            \"has(rules.lte) && rules.lte >= rules.gt && (this > rules.lte || this <= rules.gt)\"\n                \"? 'value must be greater than %s and less than or equal to %s'.format([rules.gt, rules.lte]) : ''\"\n      },\n      (predefined).cel = {\n        id: \"sfixed32.gt_lte_exclusive\"\n        expression:\n            \"has(rules.lte) && rules.lte < rules.gt && (rules.lte < this && this <= rules.gt)\"\n                \"? 'value must be greater than %s or less than or equal to %s'.format([rules.gt, rules.lte]) : ''\"\n      }\n    ];\n\n    // `gte` requires the field value to be greater than or equal to the specified\n    // value (exclusive). If the value of `gte` is larger than a specified `lt`\n    // or `lte`, the range is reversed, and the field value must be outside the\n    // specified range. If the field value doesn't meet the required conditions,\n    // an error message is generated.\n    //\n    // ```proto\n    // message MySFixed32 {\n    //   // value must be greater than or equal to 5 [sfixed32.gte]\n    //   sfixed32 value = 1 [(buf.validate.field).sfixed32.gte = 5];\n    //\n    //   // value must be greater than or equal to 5 and less than 10 [sfixed32.gte_lt]\n    //   sfixed32 other_value = 2 [(buf.validate.field).sfixed32 = { gte: 5, lt: 10 }];\n    //\n    //   // value must be greater than or equal to 10 or less than 5 [sfixed32.gte_lt_exclusive]\n    //   sfixed32 another_value = 3 [(buf.validate.field).sfixed32 = { gte: 10, lt: 5 }];\n    // }\n    // ```\n    sfixed32 gte = 5 [\n      (predefined).cel = {\n        id: \"sfixed32.gte\"\n        expression:\n            \"!has(rules.lt) && !has(rules.lte) && this < rules.gte\"\n                \"? 'value must be greater than or equal to %s'.format([rules.gte]) : ''\"\n      },\n      (predefined).cel = {\n        id: \"sfixed32.gte_lt\"\n        expression:\n            \"has(rules.lt) && rules.lt >= rules.gte && (this >= rules.lt || this < rules.gte)\"\n                \"? 'value must be greater than or equal to %s and less than %s'.format([rules.gte, rules.lt]) : ''\"\n      },\n      (predefined).cel = {\n        id: \"sfixed32.gte_lt_exclusive\"\n        expression:\n            \"has(rules.lt) && rules.lt < rules.gte && (rules.lt <= this && this < rules.gte)\"\n                \"? 'value must be greater than or equal to %s or less than %s'.format([rules.gte, rules.lt]) : ''\"\n      },\n      (predefined).cel = {\n        id: \"sfixed32.gte_lte\"\n        expression:\n            \"has(rules.lte) && rules.lte >= rules.gte && (this > rules.lte || this < rules.gte)\"\n                \"? 'value must be greater than or equal to %s and less than or equal to %s'.format([rules.gte, rules.lte]) : ''\"\n      },\n      (predefined).cel = {\n        id: \"sfixed32.gte_lte_exclusive\"\n        expression:\n            \"has(rules.lte) && rules.lte < rules.gte && (rules.lte < this && this < rules.gte)\"\n                \"? 'value must be greater than or equal to %s or less than or equal to %s'.format([rules.gte, rules.lte]) : ''\"\n      }\n    ];\n  }\n\n  // `in` requires the field value to be equal to one of the specified values.\n  // If the field value isn't one of the specified values, an error message is\n  // generated.\n  //\n  // ```proto\n  // message MySFixed32 {\n  //   // value must be in list [1, 2, 3]\n  //   repeated sfixed32 value = 1 (buf.validate.field).sfixed32 = { in: [1, 2, 3] };\n  // }\n  // ```\n  repeated sfixed32 in = 6 [(predefined).cel = {\n    id: \"sfixed32.in\"\n    expression: \"!(this in dyn(rules)['in']) ? 'value must be in list %s'.format([dyn(rules)['in']]) : ''\"\n  }];\n\n  // `not_in` requires the field value to not be equal to any of the specified\n  // values. If the field value is one of the specified values, an error\n  // message is generated.\n  //\n  // ```proto\n  // message MySFixed32 {\n  //   // value must not be in list [1, 2, 3]\n  //   repeated sfixed32 value = 1 (buf.validate.field).sfixed32 = { not_in: [1, 2, 3] };\n  // }\n  // ```\n  repeated sfixed32 not_in = 7 [(predefined).cel = {\n    id: \"sfixed32.not_in\"\n    expression: \"this in rules.not_in ? 'value must not be in list %s'.format([rules.not_in]) : ''\"\n  }];\n\n  // `example` specifies values that the field may have. These values SHOULD\n  // conform to other constraints. `example` values will not impact validation\n  // but may be used as helpful guidance on how to populate the given field.\n  //\n  // ```proto\n  // message MySFixed32 {\n  //   sfixed32 value = 1 [\n  //     (buf.validate.field).sfixed32.example = 1,\n  //     (buf.validate.field).sfixed32.example = 2\n  //   ];\n  // }\n  // ```\n  repeated sfixed32 example = 8 [(predefined).cel = {\n    id: \"sfixed32.example\"\n    expression: \"true\"\n  }];\n\n  // Extension fields in this range that have the (buf.validate.predefined)\n  // option set will be treated as predefined field constraints that can then be\n  // set on the field options of other fields to apply field constraints.\n  // Extension numbers 1000 to 99999 are reserved for extension numbers that are\n  // defined in the [Protobuf Global Extension Registry][1]. Extension numbers\n  // above this range are reserved for extension numbers that are not explicitly\n  // assigned. For rules defined in publicly-consumed schemas, use of extensions\n  // above 99999 is discouraged due to the risk of conflicts.\n  //\n  // [1]: https://github.com/protocolbuffers/protobuf/blob/main/docs/options.md\n  extensions 1000 to max;\n}\n\n// SFixed64Rules describes the constraints applied to `fixed64` values.\nmessage SFixed64Rules {\n  // `const` requires the field value to exactly match the specified value. If\n  // the field value doesn't match, an error message is generated.\n  //\n  // ```proto\n  // message MySFixed64 {\n  //   // value must equal 42\n  //   sfixed64 value = 1 [(buf.validate.field).sfixed64.const = 42];\n  // }\n  // ```\n  optional sfixed64 const = 1 [(predefined).cel = {\n    id: \"sfixed64.const\"\n    expression: \"this != rules.const ? 'value must equal %s'.format([rules.const]) : ''\"\n  }];\n  oneof less_than {\n    // `lt` requires the field value to be less than the specified value (field <\n    // value). If the field value is equal to or greater than the specified value,\n    // an error message is generated.\n    //\n    // ```proto\n    // message MySFixed64 {\n    //   // value must be less than 10\n    //   sfixed64 value = 1 [(buf.validate.field).sfixed64.lt = 10];\n    // }\n    // ```\n    sfixed64 lt = 2 [(predefined).cel = {\n      id: \"sfixed64.lt\"\n      expression:\n          \"!has(rules.gte) && !has(rules.gt) && this >= rules.lt\"\n              \"? 'value must be less than %s'.format([rules.lt]) : ''\"\n    }];\n\n    // `lte` requires the field value to be less than or equal to the specified\n    // value (field <= value). If the field value is greater than the specified\n    // value, an error message is generated.\n    //\n    // ```proto\n    // message MySFixed64 {\n    //   // value must be less than or equal to 10\n    //   sfixed64 value = 1 [(buf.validate.field).sfixed64.lte = 10];\n    // }\n    // ```\n    sfixed64 lte = 3 [(predefined).cel = {\n      id: \"sfixed64.lte\"\n      expression:\n          \"!has(rules.gte) && !has(rules.gt) && this > rules.lte\"\n              \"? 'value must be less than or equal to %s'.format([rules.lte]) : ''\"\n    }];\n  }\n  oneof greater_than {\n    // `gt` requires the field value to be greater than the specified value\n    // (exclusive). If the value of `gt` is larger than a specified `lt` or\n    // `lte`, the range is reversed, and the field value must be outside the\n    // specified range. If the field value doesn't meet the required conditions,\n    // an error message is generated.\n    //\n    // ```proto\n    // message MySFixed64 {\n    //   // value must be greater than 5 [sfixed64.gt]\n    //   sfixed64 value = 1 [(buf.validate.field).sfixed64.gt = 5];\n    //\n    //   // value must be greater than 5 and less than 10 [sfixed64.gt_lt]\n    //   sfixed64 other_value = 2 [(buf.validate.field).sfixed64 = { gt: 5, lt: 10 }];\n    //\n    //   // value must be greater than 10 or less than 5 [sfixed64.gt_lt_exclusive]\n    //   sfixed64 another_value = 3 [(buf.validate.field).sfixed64 = { gt: 10, lt: 5 }];\n    // }\n    // ```\n    sfixed64 gt = 4 [\n      (predefined).cel = {\n        id: \"sfixed64.gt\"\n        expression:\n            \"!has(rules.lt) && !has(rules.lte) && this <= rules.gt\"\n                \"? 'value must be greater than %s'.format([rules.gt]) : ''\"\n      },\n      (predefined).cel = {\n        id: \"sfixed64.gt_lt\"\n        expression:\n            \"has(rules.lt) && rules.lt >= rules.gt && (this >= rules.lt || this <= rules.gt)\"\n                \"? 'value must be greater than %s and less than %s'.format([rules.gt, rules.lt]) : ''\"\n      },\n      (predefined).cel = {\n        id: \"sfixed64.gt_lt_exclusive\"\n        expression:\n            \"has(rules.lt) && rules.lt < rules.gt && (rules.lt <= this && this <= rules.gt)\"\n                \"? 'value must be greater than %s or less than %s'.format([rules.gt, rules.lt]) : ''\"\n      },\n      (predefined).cel = {\n        id: \"sfixed64.gt_lte\"\n        expression:\n            \"has(rules.lte) && rules.lte >= rules.gt && (this > rules.lte || this <= rules.gt)\"\n                \"? 'value must be greater than %s and less than or equal to %s'.format([rules.gt, rules.lte]) : ''\"\n      },\n      (predefined).cel = {\n        id: \"sfixed64.gt_lte_exclusive\"\n        expression:\n            \"has(rules.lte) && rules.lte < rules.gt && (rules.lte < this && this <= rules.gt)\"\n                \"? 'value must be greater than %s or less than or equal to %s'.format([rules.gt, rules.lte]) : ''\"\n      }\n    ];\n\n    // `gte` requires the field value to be greater than or equal to the specified\n    // value (exclusive). If the value of `gte` is larger than a specified `lt`\n    // or `lte`, the range is reversed, and the field value must be outside the\n    // specified range. If the field value doesn't meet the required conditions,\n    // an error message is generated.\n    //\n    // ```proto\n    // message MySFixed64 {\n    //   // value must be greater than or equal to 5 [sfixed64.gte]\n    //   sfixed64 value = 1 [(buf.validate.field).sfixed64.gte = 5];\n    //\n    //   // value must be greater than or equal to 5 and less than 10 [sfixed64.gte_lt]\n    //   sfixed64 other_value = 2 [(buf.validate.field).sfixed64 = { gte: 5, lt: 10 }];\n    //\n    //   // value must be greater than or equal to 10 or less than 5 [sfixed64.gte_lt_exclusive]\n    //   sfixed64 another_value = 3 [(buf.validate.field).sfixed64 = { gte: 10, lt: 5 }];\n    // }\n    // ```\n    sfixed64 gte = 5 [\n      (predefined).cel = {\n        id: \"sfixed64.gte\"\n        expression:\n            \"!has(rules.lt) && !has(rules.lte) && this < rules.gte\"\n                \"? 'value must be greater than or equal to %s'.format([rules.gte]) : ''\"\n      },\n      (predefined).cel = {\n        id: \"sfixed64.gte_lt\"\n        expression:\n            \"has(rules.lt) && rules.lt >= rules.gte && (this >= rules.lt || this < rules.gte)\"\n                \"? 'value must be greater than or equal to %s and less than %s'.format([rules.gte, rules.lt]) : ''\"\n      },\n      (predefined).cel = {\n        id: \"sfixed64.gte_lt_exclusive\"\n        expression:\n            \"has(rules.lt) && rules.lt < rules.gte && (rules.lt <= this && this < rules.gte)\"\n                \"? 'value must be greater than or equal to %s or less than %s'.format([rules.gte, rules.lt]) : ''\"\n      },\n      (predefined).cel = {\n        id: \"sfixed64.gte_lte\"\n        expression:\n            \"has(rules.lte) && rules.lte >= rules.gte && (this > rules.lte || this < rules.gte)\"\n                \"? 'value must be greater than or equal to %s and less than or equal to %s'.format([rules.gte, rules.lte]) : ''\"\n      },\n      (predefined).cel = {\n        id: \"sfixed64.gte_lte_exclusive\"\n        expression:\n            \"has(rules.lte) && rules.lte < rules.gte && (rules.lte < this && this < rules.gte)\"\n                \"? 'value must be greater than or equal to %s or less than or equal to %s'.format([rules.gte, rules.lte]) : ''\"\n      }\n    ];\n  }\n\n  // `in` requires the field value to be equal to one of the specified values.\n  // If the field value isn't one of the specified values, an error message is\n  // generated.\n  //\n  // ```proto\n  // message MySFixed64 {\n  //   // value must be in list [1, 2, 3]\n  //   repeated sfixed64 value = 1 (buf.validate.field).sfixed64 = { in: [1, 2, 3] };\n  // }\n  // ```\n  repeated sfixed64 in = 6 [(predefined).cel = {\n    id: \"sfixed64.in\"\n    expression: \"!(this in dyn(rules)['in']) ? 'value must be in list %s'.format([dyn(rules)['in']]) : ''\"\n  }];\n\n  // `not_in` requires the field value to not be equal to any of the specified\n  // values. If the field value is one of the specified values, an error\n  // message is generated.\n  //\n  // ```proto\n  // message MySFixed64 {\n  //   // value must not be in list [1, 2, 3]\n  //   repeated sfixed64 value = 1 (buf.validate.field).sfixed64 = { not_in: [1, 2, 3] };\n  // }\n  // ```\n  repeated sfixed64 not_in = 7 [(predefined).cel = {\n    id: \"sfixed64.not_in\"\n    expression: \"this in rules.not_in ? 'value must not be in list %s'.format([rules.not_in]) : ''\"\n  }];\n\n  // `example` specifies values that the field may have. These values SHOULD\n  // conform to other constraints. `example` values will not impact validation\n  // but may be used as helpful guidance on how to populate the given field.\n  //\n  // ```proto\n  // message MySFixed64 {\n  //   sfixed64 value = 1 [\n  //     (buf.validate.field).sfixed64.example = 1,\n  //     (buf.validate.field).sfixed64.example = 2\n  //   ];\n  // }\n  // ```\n  repeated sfixed64 example = 8 [(predefined).cel = {\n    id: \"sfixed64.example\"\n    expression: \"true\"\n  }];\n\n  // Extension fields in this range that have the (buf.validate.predefined)\n  // option set will be treated as predefined field constraints that can then be\n  // set on the field options of other fields to apply field constraints.\n  // Extension numbers 1000 to 99999 are reserved for extension numbers that are\n  // defined in the [Protobuf Global Extension Registry][1]. Extension numbers\n  // above this range are reserved for extension numbers that are not explicitly\n  // assigned. For rules defined in publicly-consumed schemas, use of extensions\n  // above 99999 is discouraged due to the risk of conflicts.\n  //\n  // [1]: https://github.com/protocolbuffers/protobuf/blob/main/docs/options.md\n  extensions 1000 to max;\n}\n\n// BoolRules describes the constraints applied to `bool` values. These rules\n// may also be applied to the `google.protobuf.BoolValue` Well-Known-Type.\nmessage BoolRules {\n  // `const` requires the field value to exactly match the specified boolean value.\n  // If the field value doesn't match, an error message is generated.\n  //\n  // ```proto\n  // message MyBool {\n  //   // value must equal true\n  //   bool value = 1 [(buf.validate.field).bool.const = true];\n  // }\n  // ```\n  optional bool const = 1 [(predefined).cel = {\n    id: \"bool.const\"\n    expression: \"this != rules.const ? 'value must equal %s'.format([rules.const]) : ''\"\n  }];\n\n  // `example` specifies values that the field may have. These values SHOULD\n  // conform to other constraints. `example` values will not impact validation\n  // but may be used as helpful guidance on how to populate the given field.\n  //\n  // ```proto\n  // message MyBool {\n  //   bool value = 1 [\n  //     (buf.validate.field).bool.example = 1,\n  //     (buf.validate.field).bool.example = 2\n  //   ];\n  // }\n  // ```\n  repeated bool example = 2 [(predefined).cel = {\n    id: \"bool.example\"\n    expression: \"true\"\n  }];\n\n  // Extension fields in this range that have the (buf.validate.predefined)\n  // option set will be treated as predefined field constraints that can then be\n  // set on the field options of other fields to apply field constraints.\n  // Extension numbers 1000 to 99999 are reserved for extension numbers that are\n  // defined in the [Protobuf Global Extension Registry][1]. Extension numbers\n  // above this range are reserved for extension numbers that are not explicitly\n  // assigned. For rules defined in publicly-consumed schemas, use of extensions\n  // above 99999 is discouraged due to the risk of conflicts.\n  //\n  // [1]: https://github.com/protocolbuffers/protobuf/blob/main/docs/options.md\n  extensions 1000 to max;\n}\n\n// StringRules describes the constraints applied to `string` values These\n// rules may also be applied to the `google.protobuf.StringValue` Well-Known-Type.\nmessage StringRules {\n  // `const` requires the field value to exactly match the specified value. If\n  // the field value doesn't match, an error message is generated.\n  //\n  // ```proto\n  // message MyString {\n  //   // value must equal `hello`\n  //   string value = 1 [(buf.validate.field).string.const = \"hello\"];\n  // }\n  // ```\n  optional string const = 1 [(predefined).cel = {\n    id: \"string.const\"\n    expression: \"this != rules.const ? 'value must equal `%s`'.format([rules.const]) : ''\"\n  }];\n\n  // `len` dictates that the field value must have the specified\n  // number of characters (Unicode code points), which may differ from the number\n  // of bytes in the string. If the field value does not meet the specified\n  // length, an error message will be generated.\n  //\n  // ```proto\n  // message MyString {\n  //   // value length must be 5 characters\n  //   string value = 1 [(buf.validate.field).string.len = 5];\n  // }\n  // ```\n  optional uint64 len = 19 [(predefined).cel = {\n    id: \"string.len\"\n    expression: \"uint(this.size()) != rules.len ? 'value length must be %s characters'.format([rules.len]) : ''\"\n  }];\n\n  // `min_len` specifies that the field value must have at least the specified\n  // number of characters (Unicode code points), which may differ from the number\n  // of bytes in the string. If the field value contains fewer characters, an error\n  // message will be generated.\n  //\n  // ```proto\n  // message MyString {\n  //   // value length must be at least 3 characters\n  //   string value = 1 [(buf.validate.field).string.min_len = 3];\n  // }\n  // ```\n  optional uint64 min_len = 2 [(predefined).cel = {\n    id: \"string.min_len\"\n    expression: \"uint(this.size()) < rules.min_len ? 'value length must be at least %s characters'.format([rules.min_len]) : ''\"\n  }];\n\n  // `max_len` specifies that the field value must have no more than the specified\n  // number of characters (Unicode code points), which may differ from the\n  // number of bytes in the string. If the field value contains more characters,\n  // an error message will be generated.\n  //\n  // ```proto\n  // message MyString {\n  //   // value length must be at most 10 characters\n  //   string value = 1 [(buf.validate.field).string.max_len = 10];\n  // }\n  // ```\n  optional uint64 max_len = 3 [(predefined).cel = {\n    id: \"string.max_len\"\n    expression: \"uint(this.size()) > rules.max_len ? 'value length must be at most %s characters'.format([rules.max_len]) : ''\"\n  }];\n\n  // `len_bytes` dictates that the field value must have the specified number of\n  // bytes. If the field value does not match the specified length in bytes,\n  // an error message will be generated.\n  //\n  // ```proto\n  // message MyString {\n  //   // value length must be 6 bytes\n  //   string value = 1 [(buf.validate.field).string.len_bytes = 6];\n  // }\n  // ```\n  optional uint64 len_bytes = 20 [(predefined).cel = {\n    id: \"string.len_bytes\"\n    expression: \"uint(bytes(this).size()) != rules.len_bytes ? 'value length must be %s bytes'.format([rules.len_bytes]) : ''\"\n  }];\n\n  // `min_bytes` specifies that the field value must have at least the specified\n  // number of bytes. If the field value contains fewer bytes, an error message\n  // will be generated.\n  //\n  // ```proto\n  // message MyString {\n  //   // value length must be at least 4 bytes\n  //   string value = 1 [(buf.validate.field).string.min_bytes = 4];\n  // }\n  //\n  // ```\n  optional uint64 min_bytes = 4 [(predefined).cel = {\n    id: \"string.min_bytes\"\n    expression: \"uint(bytes(this).size()) < rules.min_bytes ? 'value length must be at least %s bytes'.format([rules.min_bytes]) : ''\"\n  }];\n\n  // `max_bytes` specifies that the field value must have no more than the\n  //specified number of bytes. If the field value contains more bytes, an\n  // error message will be generated.\n  //\n  // ```proto\n  // message MyString {\n  //   // value length must be at most 8 bytes\n  //   string value = 1 [(buf.validate.field).string.max_bytes = 8];\n  // }\n  // ```\n  optional uint64 max_bytes = 5 [(predefined).cel = {\n    id: \"string.max_bytes\"\n    expression: \"uint(bytes(this).size()) > rules.max_bytes ? 'value length must be at most %s bytes'.format([rules.max_bytes]) : ''\"\n  }];\n\n  // `pattern` specifies that the field value must match the specified\n  // regular expression (RE2 syntax), with the expression provided without any\n  // delimiters. If the field value doesn't match the regular expression, an\n  // error message will be generated.\n  //\n  // ```proto\n  // message MyString {\n  //   // value does not match regex pattern `^[a-zA-Z]//$`\n  //   string value = 1 [(buf.validate.field).string.pattern = \"^[a-zA-Z]//$\"];\n  // }\n  // ```\n  optional string pattern = 6 [(predefined).cel = {\n    id: \"string.pattern\"\n    expression: \"!this.matches(rules.pattern) ? 'value does not match regex pattern `%s`'.format([rules.pattern]) : ''\"\n  }];\n\n  // `prefix` specifies that the field value must have the\n  //specified substring at the beginning of the string. If the field value\n  // doesn't start with the specified prefix, an error message will be\n  // generated.\n  //\n  // ```proto\n  // message MyString {\n  //   // value does not have prefix `pre`\n  //   string value = 1 [(buf.validate.field).string.prefix = \"pre\"];\n  // }\n  // ```\n  optional string prefix = 7 [(predefined).cel = {\n    id: \"string.prefix\"\n    expression: \"!this.startsWith(rules.prefix) ? 'value does not have prefix `%s`'.format([rules.prefix]) : ''\"\n  }];\n\n  // `suffix` specifies that the field value must have the\n  //specified substring at the end of the string. If the field value doesn't\n  // end with the specified suffix, an error message will be generated.\n  //\n  // ```proto\n  // message MyString {\n  //   // value does not have suffix `post`\n  //   string value = 1 [(buf.validate.field).string.suffix = \"post\"];\n  // }\n  // ```\n  optional string suffix = 8 [(predefined).cel = {\n    id: \"string.suffix\"\n    expression: \"!this.endsWith(rules.suffix) ? 'value does not have suffix `%s`'.format([rules.suffix]) : ''\"\n  }];\n\n  // `contains` specifies that the field value must have the\n  //specified substring anywhere in the string. If the field value doesn't\n  // contain the specified substring, an error message will be generated.\n  //\n  // ```proto\n  // message MyString {\n  //   // value does not contain substring `inside`.\n  //   string value = 1 [(buf.validate.field).string.contains = \"inside\"];\n  // }\n  // ```\n  optional string contains = 9 [(predefined).cel = {\n    id: \"string.contains\"\n    expression: \"!this.contains(rules.contains) ? 'value does not contain substring `%s`'.format([rules.contains]) : ''\"\n  }];\n\n  // `not_contains` specifies that the field value must not have the\n  //specified substring anywhere in the string. If the field value contains\n  // the specified substring, an error message will be generated.\n  //\n  // ```proto\n  // message MyString {\n  //   // value contains substring `inside`.\n  //   string value = 1 [(buf.validate.field).string.not_contains = \"inside\"];\n  // }\n  // ```\n  optional string not_contains = 23 [(predefined).cel = {\n    id: \"string.not_contains\"\n    expression: \"this.contains(rules.not_contains) ? 'value contains substring `%s`'.format([rules.not_contains]) : ''\"\n  }];\n\n  // `in` specifies that the field value must be equal to one of the specified\n  // values. If the field value isn't one of the specified values, an error\n  // message will be generated.\n  //\n  // ```proto\n  // message MyString {\n  //   // value must be in list [\"apple\", \"banana\"]\n  //   repeated string value = 1 [(buf.validate.field).string.in = \"apple\", (buf.validate.field).string.in = \"banana\"];\n  // }\n  // ```\n  repeated string in = 10 [(predefined).cel = {\n    id: \"string.in\"\n    expression: \"!(this in dyn(rules)['in']) ? 'value must be in list %s'.format([dyn(rules)['in']]) : ''\"\n  }];\n\n  // `not_in` specifies that the field value cannot be equal to any\n  // of the specified values. If the field value is one of the specified values,\n  // an error message will be generated.\n  // ```proto\n  // message MyString {\n  //   // value must not be in list [\"orange\", \"grape\"]\n  //   repeated string value = 1 [(buf.validate.field).string.not_in = \"orange\", (buf.validate.field).string.not_in = \"grape\"];\n  // }\n  // ```\n  repeated string not_in = 11 [(predefined).cel = {\n    id: \"string.not_in\"\n    expression: \"this in rules.not_in ? 'value must not be in list %s'.format([rules.not_in]) : ''\"\n  }];\n\n  // `WellKnown` rules provide advanced constraints against common string\n  // patterns\n  oneof well_known {\n    // `email` specifies that the field value must be a valid email address\n    // (addr-spec only) as defined by [RFC 5322](https://tools.ietf.org/html/rfc5322#section-3.4.1).\n    // If the field value isn't a valid email address, an error message will be generated.\n    //\n    // ```proto\n    // message MyString {\n    //   // value must be a valid email address\n    //   string value = 1 [(buf.validate.field).string.email = true];\n    // }\n    // ```\n    bool email = 12 [\n      (predefined).cel = {\n        id: \"string.email\"\n        message: \"value must be a valid email address\"\n        expression: \"!rules.email || this == '' || this.isEmail()\"\n      },\n      (predefined).cel = {\n        id: \"string.email_empty\"\n        message: \"value is empty, which is not a valid email address\"\n        expression: \"!rules.email || this != ''\"\n      }\n    ];\n\n    // `hostname` specifies that the field value must be a valid\n    // hostname as defined by [RFC 1034](https://tools.ietf.org/html/rfc1034#section-3.5). This constraint doesn't support\n    // internationalized domain names (IDNs). If the field value isn't a\n    // valid hostname, an error message will be generated.\n    //\n    // ```proto\n    // message MyString {\n    //   // value must be a valid hostname\n    //   string value = 1 [(buf.validate.field).string.hostname = true];\n    // }\n    // ```\n    bool hostname = 13 [\n      (predefined).cel = {\n        id: \"string.hostname\"\n        message: \"value must be a valid hostname\"\n        expression: \"!rules.hostname || this == '' || this.isHostname()\"\n      },\n      (predefined).cel = {\n        id: \"string.hostname_empty\"\n        message: \"value is empty, which is not a valid hostname\"\n        expression: \"!rules.hostname || this != ''\"\n      }\n    ];\n\n    // `ip` specifies that the field value must be a valid IP\n    // (v4 or v6) address, without surrounding square brackets for IPv6 addresses.\n    // If the field value isn't a valid IP address, an error message will be\n    // generated.\n    //\n    // ```proto\n    // message MyString {\n    //   // value must be a valid IP address\n    //   string value = 1 [(buf.validate.field).string.ip = true];\n    // }\n    // ```\n    bool ip = 14 [\n      (predefined).cel = {\n        id: \"string.ip\"\n        message: \"value must be a valid IP address\"\n        expression: \"!rules.ip || this == '' || this.isIp()\"\n      },\n      (predefined).cel = {\n        id: \"string.ip_empty\"\n        message: \"value is empty, which is not a valid IP address\"\n        expression: \"!rules.ip || this != ''\"\n      }\n    ];\n\n    // `ipv4` specifies that the field value must be a valid IPv4\n    // address. If the field value isn't a valid IPv4 address, an error message\n    // will be generated.\n    //\n    // ```proto\n    // message MyString {\n    //   // value must be a valid IPv4 address\n    //   string value = 1 [(buf.validate.field).string.ipv4 = true];\n    // }\n    // ```\n    bool ipv4 = 15 [\n      (predefined).cel = {\n        id: \"string.ipv4\"\n        message: \"value must be a valid IPv4 address\"\n        expression: \"!rules.ipv4 || this == '' || this.isIp(4)\"\n      },\n      (predefined).cel = {\n        id: \"string.ipv4_empty\"\n        message: \"value is empty, which is not a valid IPv4 address\"\n        expression: \"!rules.ipv4 || this != ''\"\n      }\n    ];\n\n    // `ipv6` specifies that the field value must be a valid\n    // IPv6 address, without surrounding square brackets. If the field value is\n    // not a valid IPv6 address, an error message will be generated.\n    //\n    // ```proto\n    // message MyString {\n    //   // value must be a valid IPv6 address\n    //   string value = 1 [(buf.validate.field).string.ipv6 = true];\n    // }\n    // ```\n    bool ipv6 = 16 [\n      (predefined).cel = {\n        id: \"string.ipv6\"\n        message: \"value must be a valid IPv6 address\"\n        expression: \"!rules.ipv6 || this == '' || this.isIp(6)\"\n      },\n      (predefined).cel = {\n        id: \"string.ipv6_empty\"\n        message: \"value is empty, which is not a valid IPv6 address\"\n        expression: \"!rules.ipv6 || this != ''\"\n      }\n    ];\n\n    // `uri` specifies that the field value must be a valid,\n    // absolute URI as defined by [RFC 3986](https://tools.ietf.org/html/rfc3986#section-3). If the field value isn't a valid,\n    // absolute URI, an error message will be generated.\n    //\n    // ```proto\n    // message MyString {\n    //   // value must be a valid URI\n    //   string value = 1 [(buf.validate.field).string.uri = true];\n    // }\n    // ```\n    bool uri = 17 [\n      (predefined).cel = {\n        id: \"string.uri\"\n        message: \"value must be a valid URI\"\n        expression: \"!rules.uri || this == '' || this.isUri()\"\n      },\n      (predefined).cel = {\n        id: \"string.uri_empty\"\n        message: \"value is empty, which is not a valid URI\"\n        expression: \"!rules.uri || this != ''\"\n      }\n    ];\n\n    // `uri_ref` specifies that the field value must be a valid URI\n    // as defined by [RFC 3986](https://tools.ietf.org/html/rfc3986#section-3) and may be either relative or absolute. If the\n    // field value isn't a valid URI, an error message will be generated.\n    //\n    // ```proto\n    // message MyString {\n    //   // value must be a valid URI\n    //   string value = 1 [(buf.validate.field).string.uri_ref = true];\n    // }\n    // ```\n    bool uri_ref = 18 [(predefined).cel = {\n      id: \"string.uri_ref\"\n      message: \"value must be a valid URI\"\n      expression: \"!rules.uri_ref || this.isUriRef()\"\n    }];\n\n    // `address` specifies that the field value must be either a valid hostname\n    // as defined by [RFC 1034](https://tools.ietf.org/html/rfc1034#section-3.5)\n    // (which doesn't support internationalized domain names or IDNs) or a valid\n    // IP (v4 or v6). If the field value isn't a valid hostname or IP, an error\n    // message will be generated.\n    //\n    // ```proto\n    // message MyString {\n    //   // value must be a valid hostname, or ip address\n    //   string value = 1 [(buf.validate.field).string.address = true];\n    // }\n    // ```\n    bool address = 21 [\n      (predefined).cel = {\n        id: \"string.address\"\n        message: \"value must be a valid hostname, or ip address\"\n        expression: \"!rules.address || this == '' || this.isHostname() || this.isIp()\"\n      },\n      (predefined).cel = {\n        id: \"string.address_empty\"\n        message: \"value is empty, which is not a valid hostname, or ip address\"\n        expression: \"!rules.address || this != ''\"\n      }\n    ];\n\n    // `uuid` specifies that the field value must be a valid UUID as defined by\n    // [RFC 4122](https://tools.ietf.org/html/rfc4122#section-4.1.2). If the\n    // field value isn't a valid UUID, an error message will be generated.\n    //\n    // ```proto\n    // message MyString {\n    //   // value must be a valid UUID\n    //   string value = 1 [(buf.validate.field).string.uuid = true];\n    // }\n    // ```\n    bool uuid = 22 [\n      (predefined).cel = {\n        id: \"string.uuid\"\n        message: \"value must be a valid UUID\"\n        expression: \"!rules.uuid || this == '' || this.matches('^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$')\"\n      },\n      (predefined).cel = {\n        id: \"string.uuid_empty\"\n        message: \"value is empty, which is not a valid UUID\"\n        expression: \"!rules.uuid || this != ''\"\n      }\n    ];\n\n    // `tuuid` (trimmed UUID) specifies that the field value must be a valid UUID as\n    // defined by [RFC 4122](https://tools.ietf.org/html/rfc4122#section-4.1.2) with all dashes\n    // omitted. If the field value isn't a valid UUID without dashes, an error message\n    // will be generated.\n    //\n    // ```proto\n    // message MyString {\n    //   // value must be a valid trimmed UUID\n    //   string value = 1 [(buf.validate.field).string.tuuid = true];\n    // }\n    // ```\n    bool tuuid = 33 [\n      (predefined).cel = {\n        id: \"string.tuuid\"\n        message: \"value must be a valid trimmed UUID\"\n        expression: \"!rules.tuuid || this == '' || this.matches('^[0-9a-fA-F]{32}$')\"\n      },\n      (predefined).cel = {\n        id: \"string.tuuid_empty\"\n        message: \"value is empty, which is not a valid trimmed UUID\"\n        expression: \"!rules.tuuid || this != ''\"\n      }\n    ];\n\n    // `ip_with_prefixlen` specifies that the field value must be a valid IP (v4 or v6)\n    // address with prefix length. If the field value isn't a valid IP with prefix\n    // length, an error message will be generated.\n    //\n    //\n    // ```proto\n    // message MyString {\n    //   // value must be a valid IP with prefix length\n    //    string value = 1 [(buf.validate.field).string.ip_with_prefixlen = true];\n    // }\n    // ```\n    bool ip_with_prefixlen = 26 [\n      (predefined).cel = {\n        id: \"string.ip_with_prefixlen\"\n        message: \"value must be a valid IP prefix\"\n        expression: \"!rules.ip_with_prefixlen || this == '' || this.isIpPrefix()\"\n      },\n      (predefined).cel = {\n        id: \"string.ip_with_prefixlen_empty\"\n        message: \"value is empty, which is not a valid IP prefix\"\n        expression: \"!rules.ip_with_prefixlen || this != ''\"\n      }\n    ];\n\n    // `ipv4_with_prefixlen` specifies that the field value must be a valid\n    // IPv4 address with prefix.\n    // If the field value isn't a valid IPv4 address with prefix length,\n    // an error message will be generated.\n    //\n    // ```proto\n    // message MyString {\n    //   // value must be a valid IPv4 address with prefix length\n    //    string value = 1 [(buf.validate.field).string.ipv4_with_prefixlen = true];\n    // }\n    // ```\n    bool ipv4_with_prefixlen = 27 [\n      (predefined).cel = {\n        id: \"string.ipv4_with_prefixlen\"\n        message: \"value must be a valid IPv4 address with prefix length\"\n        expression: \"!rules.ipv4_with_prefixlen || this == '' || this.isIpPrefix(4)\"\n      },\n      (predefined).cel = {\n        id: \"string.ipv4_with_prefixlen_empty\"\n        message: \"value is empty, which is not a valid IPv4 address with prefix length\"\n        expression: \"!rules.ipv4_with_prefixlen || this != ''\"\n      }\n    ];\n\n    // `ipv6_with_prefixlen` specifies that the field value must be a valid\n    // IPv6 address with prefix length.\n    // If the field value is not a valid IPv6 address with prefix length,\n    // an error message will be generated.\n    //\n    // ```proto\n    // message MyString {\n    //   // value must be a valid IPv6 address prefix length\n    //    string value = 1 [(buf.validate.field).string.ipv6_with_prefixlen = true];\n    // }\n    // ```\n    bool ipv6_with_prefixlen = 28 [\n      (predefined).cel = {\n        id: \"string.ipv6_with_prefixlen\"\n        message: \"value must be a valid IPv6 address with prefix length\"\n        expression: \"!rules.ipv6_with_prefixlen || this == '' || this.isIpPrefix(6)\"\n      },\n      (predefined).cel = {\n        id: \"string.ipv6_with_prefixlen_empty\"\n        message: \"value is empty, which is not a valid IPv6 address with prefix length\"\n        expression: \"!rules.ipv6_with_prefixlen || this != ''\"\n      }\n    ];\n\n    // `ip_prefix` specifies that the field value must be a valid IP (v4 or v6) prefix.\n    // If the field value isn't a valid IP prefix, an error message will be\n    // generated. The prefix must have all zeros for the masked bits of the prefix (e.g.,\n    // `127.0.0.0/16`, not `127.0.0.1/16`).\n    //\n    // ```proto\n    // message MyString {\n    //   // value must be a valid IP prefix\n    //    string value = 1 [(buf.validate.field).string.ip_prefix = true];\n    // }\n    // ```\n    bool ip_prefix = 29 [\n      (predefined).cel = {\n        id: \"string.ip_prefix\"\n        message: \"value must be a valid IP prefix\"\n        expression: \"!rules.ip_prefix || this == '' || this.isIpPrefix(true)\"\n      },\n      (predefined).cel = {\n        id: \"string.ip_prefix_empty\"\n        message: \"value is empty, which is not a valid IP prefix\"\n        expression: \"!rules.ip_prefix || this != ''\"\n      }\n    ];\n\n    // `ipv4_prefix` specifies that the field value must be a valid IPv4\n    // prefix. If the field value isn't a valid IPv4 prefix, an error message\n    // will be generated. The prefix must have all zeros for the masked bits of\n    // the prefix (e.g., `127.0.0.0/16`, not `127.0.0.1/16`).\n    //\n    // ```proto\n    // message MyString {\n    //   // value must be a valid IPv4 prefix\n    //    string value = 1 [(buf.validate.field).string.ipv4_prefix = true];\n    // }\n    // ```\n    bool ipv4_prefix = 30 [\n      (predefined).cel = {\n        id: \"string.ipv4_prefix\"\n        message: \"value must be a valid IPv4 prefix\"\n        expression: \"!rules.ipv4_prefix || this == '' || this.isIpPrefix(4, true)\"\n      },\n      (predefined).cel = {\n        id: \"string.ipv4_prefix_empty\"\n        message: \"value is empty, which is not a valid IPv4 prefix\"\n        expression: \"!rules.ipv4_prefix || this != ''\"\n      }\n    ];\n\n    // `ipv6_prefix` specifies that the field value must be a valid IPv6 prefix.\n    // If the field value is not a valid IPv6 prefix, an error message will be\n    // generated. The prefix must have all zeros for the masked bits of the prefix\n    // (e.g., `2001:db8::/48`, not `2001:db8::1/48`).\n    //\n    // ```proto\n    // message MyString {\n    //   // value must be a valid IPv6 prefix\n    //    string value = 1 [(buf.validate.field).string.ipv6_prefix = true];\n    // }\n    // ```\n    bool ipv6_prefix = 31 [\n      (predefined).cel = {\n        id: \"string.ipv6_prefix\"\n        message: \"value must be a valid IPv6 prefix\"\n        expression: \"!rules.ipv6_prefix || this == '' || this.isIpPrefix(6, true)\"\n      },\n      (predefined).cel = {\n        id: \"string.ipv6_prefix_empty\"\n        message: \"value is empty, which is not a valid IPv6 prefix\"\n        expression: \"!rules.ipv6_prefix || this != ''\"\n      }\n    ];\n\n    // `host_and_port` specifies the field value must be a valid host and port\n    // pair. The host must be a valid hostname or IP address while the port\n    // must be in the range of 0-65535, inclusive. IPv6 addresses must be delimited\n    // with square brackets (e.g., `[::1]:1234`).\n    bool host_and_port = 32 [\n      (predefined).cel = {\n        id: \"string.host_and_port\"\n        message: \"value must be a valid host (hostname or IP address) and port pair\"\n        expression: \"!rules.host_and_port || this == '' || this.isHostAndPort(true)\"\n      },\n      (predefined).cel = {\n        id: \"string.host_and_port_empty\"\n        message: \"value is empty, which is not a valid host and port pair\"\n        expression: \"!rules.host_and_port || this != ''\"\n      }\n    ];\n\n    // `well_known_regex` specifies a common well-known pattern\n    // defined as a regex. If the field value doesn't match the well-known\n    // regex, an error message will be generated.\n    //\n    // ```proto\n    // message MyString {\n    //   // value must be a valid HTTP header value\n    //   string value = 1 [(buf.validate.field).string.well_known_regex = KNOWN_REGEX_HTTP_HEADER_VALUE];\n    // }\n    // ```\n    //\n    // #### KnownRegex\n    //\n    // `well_known_regex` contains some well-known patterns.\n    //\n    // | Name                          | Number | Description                               |\n    // |-------------------------------|--------|-------------------------------------------|\n    // | KNOWN_REGEX_UNSPECIFIED       | 0      |                                           |\n    // | KNOWN_REGEX_HTTP_HEADER_NAME  | 1      | HTTP header name as defined by [RFC 7230](https://tools.ietf.org/html/rfc7230#section-3.2)  |\n    // | KNOWN_REGEX_HTTP_HEADER_VALUE | 2      | HTTP header value as defined by [RFC 7230](https://tools.ietf.org/html/rfc7230#section-3.2.4) |\n    KnownRegex well_known_regex = 24 [\n      (predefined).cel = {\n        id: \"string.well_known_regex.header_name\"\n        message: \"value must be a valid HTTP header name\"\n        expression:\n            \"rules.well_known_regex != 1 || this == '' || this.matches(!has(rules.strict) || rules.strict ?\"\n                \"'^:?[0-9a-zA-Z!#$%&\\\\'*+-.^_|~\\\\x60]+$' :\"\n                \"'^[^\\\\u0000\\\\u000A\\\\u000D]+$')\"\n      },\n      (predefined).cel = {\n        id: \"string.well_known_regex.header_name_empty\"\n        message: \"value is empty, which is not a valid HTTP header name\"\n        expression: \"rules.well_known_regex != 1 || this != ''\"\n      },\n      (predefined).cel = {\n        id: \"string.well_known_regex.header_value\"\n        message: \"value must be a valid HTTP header value\"\n        expression:\n            \"rules.well_known_regex != 2 || this.matches(!has(rules.strict) || rules.strict ?\"\n                \"'^[^\\\\u0000-\\\\u0008\\\\u000A-\\\\u001F\\\\u007F]*$' :\"\n                \"'^[^\\\\u0000\\\\u000A\\\\u000D]*$')\"\n      }\n    ];\n  }\n\n  // This applies to regexes `HTTP_HEADER_NAME` and `HTTP_HEADER_VALUE` to\n  // enable strict header validation. By default, this is true, and HTTP header\n  // validations are [RFC-compliant](https://tools.ietf.org/html/rfc7230#section-3). Setting to false will enable looser\n  // validations that only disallow `\\r\\n\\0` characters, which can be used to\n  // bypass header matching rules.\n  //\n  // ```proto\n  // message MyString {\n  //   // The field `value` must have be a valid HTTP headers, but not enforced with strict rules.\n  //   string value = 1 [(buf.validate.field).string.strict = false];\n  // }\n  // ```\n  optional bool strict = 25;\n\n  // `example` specifies values that the field may have. These values SHOULD\n  // conform to other constraints. `example` values will not impact validation\n  // but may be used as helpful guidance on how to populate the given field.\n  //\n  // ```proto\n  // message MyString {\n  //   string value = 1 [\n  //     (buf.validate.field).string.example = \"hello\",\n  //     (buf.validate.field).string.example = \"world\"\n  //   ];\n  // }\n  // ```\n  repeated string example = 34 [(predefined).cel = {\n    id: \"string.example\"\n    expression: \"true\"\n  }];\n\n  // Extension fields in this range that have the (buf.validate.predefined)\n  // option set will be treated as predefined field constraints that can then be\n  // set on the field options of other fields to apply field constraints.\n  // Extension numbers 1000 to 99999 are reserved for extension numbers that are\n  // defined in the [Protobuf Global Extension Registry][1]. Extension numbers\n  // above this range are reserved for extension numbers that are not explicitly\n  // assigned. For rules defined in publicly-consumed schemas, use of extensions\n  // above 99999 is discouraged due to the risk of conflicts.\n  //\n  // [1]: https://github.com/protocolbuffers/protobuf/blob/main/docs/options.md\n  extensions 1000 to max;\n}\n\n// WellKnownRegex contain some well-known patterns.\nenum KnownRegex {\n  KNOWN_REGEX_UNSPECIFIED = 0;\n\n  // HTTP header name as defined by [RFC 7230](https://tools.ietf.org/html/rfc7230#section-3.2).\n  KNOWN_REGEX_HTTP_HEADER_NAME = 1;\n\n  // HTTP header value as defined by [RFC 7230](https://tools.ietf.org/html/rfc7230#section-3.2.4).\n  KNOWN_REGEX_HTTP_HEADER_VALUE = 2;\n}\n\n// BytesRules describe the constraints applied to `bytes` values. These rules\n// may also be applied to the `google.protobuf.BytesValue` Well-Known-Type.\nmessage BytesRules {\n  // `const` requires the field value to exactly match the specified bytes\n  // value. If the field value doesn't match, an error message is generated.\n  //\n  // ```proto\n  // message MyBytes {\n  //   // value must be \"\\x01\\x02\\x03\\x04\"\n  //   bytes value = 1 [(buf.validate.field).bytes.const = \"\\x01\\x02\\x03\\x04\"];\n  // }\n  // ```\n  optional bytes const = 1 [(predefined).cel = {\n    id: \"bytes.const\"\n    expression: \"this != rules.const ? 'value must be %x'.format([rules.const]) : ''\"\n  }];\n\n  // `len` requires the field value to have the specified length in bytes.\n  // If the field value doesn't match, an error message is generated.\n  //\n  // ```proto\n  // message MyBytes {\n  //   // value length must be 4 bytes.\n  //   optional bytes value = 1 [(buf.validate.field).bytes.len = 4];\n  // }\n  // ```\n  optional uint64 len = 13 [(predefined).cel = {\n    id: \"bytes.len\"\n    expression: \"uint(this.size()) != rules.len ? 'value length must be %s bytes'.format([rules.len]) : ''\"\n  }];\n\n  // `min_len` requires the field value to have at least the specified minimum\n  // length in bytes.\n  // If the field value doesn't meet the requirement, an error message is generated.\n  //\n  // ```proto\n  // message MyBytes {\n  //   // value length must be at least 2 bytes.\n  //   optional bytes value = 1 [(buf.validate.field).bytes.min_len = 2];\n  // }\n  // ```\n  optional uint64 min_len = 2 [(predefined).cel = {\n    id: \"bytes.min_len\"\n    expression: \"uint(this.size()) < rules.min_len ? 'value length must be at least %s bytes'.format([rules.min_len]) : ''\"\n  }];\n\n  // `max_len` requires the field value to have at most the specified maximum\n  // length in bytes.\n  // If the field value exceeds the requirement, an error message is generated.\n  //\n  // ```proto\n  // message MyBytes {\n  //   // value must be at most 6 bytes.\n  //   optional bytes value = 1 [(buf.validate.field).bytes.max_len = 6];\n  // }\n  // ```\n  optional uint64 max_len = 3 [(predefined).cel = {\n    id: \"bytes.max_len\"\n    expression: \"uint(this.size()) > rules.max_len ? 'value must be at most %s bytes'.format([rules.max_len]) : ''\"\n  }];\n\n  // `pattern` requires the field value to match the specified regular\n  // expression ([RE2 syntax](https://github.com/google/re2/wiki/Syntax)).\n  // The value of the field must be valid UTF-8 or validation will fail with a\n  // runtime error.\n  // If the field value doesn't match the pattern, an error message is generated.\n  //\n  // ```proto\n  // message MyBytes {\n  //   // value must match regex pattern \"^[a-zA-Z0-9]+$\".\n  //   optional bytes value = 1 [(buf.validate.field).bytes.pattern = \"^[a-zA-Z0-9]+$\"];\n  // }\n  // ```\n  optional string pattern = 4 [(predefined).cel = {\n    id: \"bytes.pattern\"\n    expression: \"!string(this).matches(rules.pattern) ? 'value must match regex pattern `%s`'.format([rules.pattern]) : ''\"\n  }];\n\n  // `prefix` requires the field value to have the specified bytes at the\n  // beginning of the string.\n  // If the field value doesn't meet the requirement, an error message is generated.\n  //\n  // ```proto\n  // message MyBytes {\n  //   // value does not have prefix \\x01\\x02\n  //   optional bytes value = 1 [(buf.validate.field).bytes.prefix = \"\\x01\\x02\"];\n  // }\n  // ```\n  optional bytes prefix = 5 [(predefined).cel = {\n    id: \"bytes.prefix\"\n    expression: \"!this.startsWith(rules.prefix) ? 'value does not have prefix %x'.format([rules.prefix]) : ''\"\n  }];\n\n  // `suffix` requires the field value to have the specified bytes at the end\n  // of the string.\n  // If the field value doesn't meet the requirement, an error message is generated.\n  //\n  // ```proto\n  // message MyBytes {\n  //   // value does not have suffix \\x03\\x04\n  //   optional bytes value = 1 [(buf.validate.field).bytes.suffix = \"\\x03\\x04\"];\n  // }\n  // ```\n  optional bytes suffix = 6 [(predefined).cel = {\n    id: \"bytes.suffix\"\n    expression: \"!this.endsWith(rules.suffix) ? 'value does not have suffix %x'.format([rules.suffix]) : ''\"\n  }];\n\n  // `contains` requires the field value to have the specified bytes anywhere in\n  // the string.\n  // If the field value doesn't meet the requirement, an error message is generated.\n  //\n  // ```protobuf\n  // message MyBytes {\n  //   // value does not contain \\x02\\x03\n  //   optional bytes value = 1 [(buf.validate.field).bytes.contains = \"\\x02\\x03\"];\n  // }\n  // ```\n  optional bytes contains = 7 [(predefined).cel = {\n    id: \"bytes.contains\"\n    expression: \"!this.contains(rules.contains) ? 'value does not contain %x'.format([rules.contains]) : ''\"\n  }];\n\n  // `in` requires the field value to be equal to one of the specified\n  // values. If the field value doesn't match any of the specified values, an\n  // error message is generated.\n  //\n  // ```protobuf\n  // message MyBytes {\n  //   // value must in [\"\\x01\\x02\", \"\\x02\\x03\", \"\\x03\\x04\"]\n  //   optional bytes value = 1 [(buf.validate.field).bytes.in = {\"\\x01\\x02\", \"\\x02\\x03\", \"\\x03\\x04\"}];\n  // }\n  // ```\n  repeated bytes in = 8 [(predefined).cel = {\n    id: \"bytes.in\"\n    expression: \"dyn(rules)['in'].size() > 0 && !(this in dyn(rules)['in']) ? 'value must be in list %s'.format([dyn(rules)['in']]) : ''\"\n  }];\n\n  // `not_in` requires the field value to be not equal to any of the specified\n  // values.\n  // If the field value matches any of the specified values, an error message is\n  // generated.\n  //\n  // ```proto\n  // message MyBytes {\n  //   // value must not in [\"\\x01\\x02\", \"\\x02\\x03\", \"\\x03\\x04\"]\n  //   optional bytes value = 1 [(buf.validate.field).bytes.not_in = {\"\\x01\\x02\", \"\\x02\\x03\", \"\\x03\\x04\"}];\n  // }\n  // ```\n  repeated bytes not_in = 9 [(predefined).cel = {\n    id: \"bytes.not_in\"\n    expression: \"this in rules.not_in ? 'value must not be in list %s'.format([rules.not_in]) : ''\"\n  }];\n\n  // WellKnown rules provide advanced constraints against common byte\n  // patterns\n  oneof well_known {\n    // `ip` ensures that the field `value` is a valid IP address (v4 or v6) in byte format.\n    // If the field value doesn't meet this constraint, an error message is generated.\n    //\n    // ```proto\n    // message MyBytes {\n    //   // value must be a valid IP address\n    //   optional bytes value = 1 [(buf.validate.field).bytes.ip = true];\n    // }\n    // ```\n    bool ip = 10 [\n      (predefined).cel = {\n        id: \"bytes.ip\"\n        message: \"value must be a valid IP address\"\n        expression: \"!rules.ip || this.size() == 0 || this.size() == 4 || this.size() == 16\"\n      },\n      (predefined).cel = {\n        id: \"bytes.ip_empty\"\n        message: \"value is empty, which is not a valid IP address\"\n        expression: \"!rules.ip || this.size() != 0\"\n      }\n    ];\n\n    // `ipv4` ensures that the field `value` is a valid IPv4 address in byte format.\n    // If the field value doesn't meet this constraint, an error message is generated.\n    //\n    // ```proto\n    // message MyBytes {\n    //   // value must be a valid IPv4 address\n    //   optional bytes value = 1 [(buf.validate.field).bytes.ipv4 = true];\n    // }\n    // ```\n    bool ipv4 = 11 [\n      (predefined).cel = {\n        id: \"bytes.ipv4\"\n        message: \"value must be a valid IPv4 address\"\n        expression: \"!rules.ipv4 || this.size() == 0 || this.size() == 4\"\n      },\n      (predefined).cel = {\n        id: \"bytes.ipv4_empty\"\n        message: \"value is empty, which is not a valid IPv4 address\"\n        expression: \"!rules.ipv4 || this.size() != 0\"\n      }\n    ];\n\n    // `ipv6` ensures that the field `value` is a valid IPv6 address in byte format.\n    // If the field value doesn't meet this constraint, an error message is generated.\n    // ```proto\n    // message MyBytes {\n    //   // value must be a valid IPv6 address\n    //   optional bytes value = 1 [(buf.validate.field).bytes.ipv6 = true];\n    // }\n    // ```\n    bool ipv6 = 12 [\n      (predefined).cel = {\n        id: \"bytes.ipv6\"\n        message: \"value must be a valid IPv6 address\"\n        expression: \"!rules.ipv6 || this.size() == 0 || this.size() == 16\"\n      },\n      (predefined).cel = {\n        id: \"bytes.ipv6_empty\"\n        message: \"value is empty, which is not a valid IPv6 address\"\n        expression: \"!rules.ipv6 || this.size() != 0\"\n      }\n    ];\n  }\n\n  // `example` specifies values that the field may have. These values SHOULD\n  // conform to other constraints. `example` values will not impact validation\n  // but may be used as helpful guidance on how to populate the given field.\n  //\n  // ```proto\n  // message MyBytes {\n  //   bytes value = 1 [\n  //     (buf.validate.field).bytes.example = \"\\x01\\x02\",\n  //     (buf.validate.field).bytes.example = \"\\x02\\x03\"\n  //   ];\n  // }\n  // ```\n  repeated bytes example = 14 [(predefined).cel = {\n    id: \"bytes.example\"\n    expression: \"true\"\n  }];\n\n  // Extension fields in this range that have the (buf.validate.predefined)\n  // option set will be treated as predefined field constraints that can then be\n  // set on the field options of other fields to apply field constraints.\n  // Extension numbers 1000 to 99999 are reserved for extension numbers that are\n  // defined in the [Protobuf Global Extension Registry][1]. Extension numbers\n  // above this range are reserved for extension numbers that are not explicitly\n  // assigned. For rules defined in publicly-consumed schemas, use of extensions\n  // above 99999 is discouraged due to the risk of conflicts.\n  //\n  // [1]: https://github.com/protocolbuffers/protobuf/blob/main/docs/options.md\n  extensions 1000 to max;\n}\n\n// EnumRules describe the constraints applied to `enum` values.\nmessage EnumRules {\n  // `const` requires the field value to exactly match the specified enum value.\n  // If the field value doesn't match, an error message is generated.\n  //\n  // ```proto\n  // enum MyEnum {\n  //   MY_ENUM_UNSPECIFIED = 0;\n  //   MY_ENUM_VALUE1 = 1;\n  //   MY_ENUM_VALUE2 = 2;\n  // }\n  //\n  // message MyMessage {\n  //   // The field `value` must be exactly MY_ENUM_VALUE1.\n  //   MyEnum value = 1 [(buf.validate.field).enum.const = 1];\n  // }\n  // ```\n  optional int32 const = 1 [(predefined).cel = {\n    id: \"enum.const\"\n    expression: \"this != rules.const ? 'value must equal %s'.format([rules.const]) : ''\"\n  }];\n\n  // `defined_only` requires the field value to be one of the defined values for\n  // this enum, failing on any undefined value.\n  //\n  // ```proto\n  // enum MyEnum {\n  //   MY_ENUM_UNSPECIFIED = 0;\n  //   MY_ENUM_VALUE1 = 1;\n  //   MY_ENUM_VALUE2 = 2;\n  // }\n  //\n  // message MyMessage {\n  //   // The field `value` must be a defined value of MyEnum.\n  //   MyEnum value = 1 [(buf.validate.field).enum.defined_only = true];\n  // }\n  // ```\n  optional bool defined_only = 2;\n\n  // `in` requires the field value to be equal to one of the\n  //specified enum values. If the field value doesn't match any of the\n  //specified values, an error message is generated.\n  //\n  // ```proto\n  // enum MyEnum {\n  //   MY_ENUM_UNSPECIFIED = 0;\n  //   MY_ENUM_VALUE1 = 1;\n  //   MY_ENUM_VALUE2 = 2;\n  // }\n  //\n  // message MyMessage {\n  //   // The field `value` must be equal to one of the specified values.\n  //   MyEnum value = 1 [(buf.validate.field).enum = { in: [1, 2]}];\n  // }\n  // ```\n  repeated int32 in = 3 [(predefined).cel = {\n    id: \"enum.in\"\n    expression: \"!(this in dyn(rules)['in']) ? 'value must be in list %s'.format([dyn(rules)['in']]) : ''\"\n  }];\n\n  // `not_in` requires the field value to be not equal to any of the\n  //specified enum values. If the field value matches one of the specified\n  // values, an error message is generated.\n  //\n  // ```proto\n  // enum MyEnum {\n  //   MY_ENUM_UNSPECIFIED = 0;\n  //   MY_ENUM_VALUE1 = 1;\n  //   MY_ENUM_VALUE2 = 2;\n  // }\n  //\n  // message MyMessage {\n  //   // The field `value` must not be equal to any of the specified values.\n  //   MyEnum value = 1 [(buf.validate.field).enum = { not_in: [1, 2]}];\n  // }\n  // ```\n  repeated int32 not_in = 4 [(predefined).cel = {\n    id: \"enum.not_in\"\n    expression: \"this in rules.not_in ? 'value must not be in list %s'.format([rules.not_in]) : ''\"\n  }];\n\n  // `example` specifies values that the field may have. These values SHOULD\n  // conform to other constraints. `example` values will not impact validation\n  // but may be used as helpful guidance on how to populate the given field.\n  //\n  // ```proto\n  // enum MyEnum {\n  //   MY_ENUM_UNSPECIFIED = 0;\n  //   MY_ENUM_VALUE1 = 1;\n  //   MY_ENUM_VALUE2 = 2;\n  // }\n  //\n  // message MyMessage {\n  //     (buf.validate.field).enum.example = 1,\n  //     (buf.validate.field).enum.example = 2\n  // }\n  // ```\n  repeated int32 example = 5 [(predefined).cel = {\n    id: \"enum.example\"\n    expression: \"true\"\n  }];\n\n  // Extension fields in this range that have the (buf.validate.predefined)\n  // option set will be treated as predefined field constraints that can then be\n  // set on the field options of other fields to apply field constraints.\n  // Extension numbers 1000 to 99999 are reserved for extension numbers that are\n  // defined in the [Protobuf Global Extension Registry][1]. Extension numbers\n  // above this range are reserved for extension numbers that are not explicitly\n  // assigned. For rules defined in publicly-consumed schemas, use of extensions\n  // above 99999 is discouraged due to the risk of conflicts.\n  //\n  // [1]: https://github.com/protocolbuffers/protobuf/blob/main/docs/options.md\n  extensions 1000 to max;\n}\n\n// RepeatedRules describe the constraints applied to `repeated` values.\nmessage RepeatedRules {\n  // `min_items` requires that this field must contain at least the specified\n  // minimum number of items.\n  //\n  // Note that `min_items = 1` is equivalent to setting a field as `required`.\n  //\n  // ```proto\n  // message MyRepeated {\n  //   // value must contain at least  2 items\n  //   repeated string value = 1 [(buf.validate.field).repeated.min_items = 2];\n  // }\n  // ```\n  optional uint64 min_items = 1 [(predefined).cel = {\n    id: \"repeated.min_items\"\n    expression: \"uint(this.size()) < rules.min_items ? 'value must contain at least %d item(s)'.format([rules.min_items]) : ''\"\n  }];\n\n  // `max_items` denotes that this field must not exceed a\n  // certain number of items as the upper limit. If the field contains more\n  // items than specified, an error message will be generated, requiring the\n  // field to maintain no more than the specified number of items.\n  //\n  // ```proto\n  // message MyRepeated {\n  //   // value must contain no more than 3 item(s)\n  //   repeated string value = 1 [(buf.validate.field).repeated.max_items = 3];\n  // }\n  // ```\n  optional uint64 max_items = 2 [(predefined).cel = {\n    id: \"repeated.max_items\"\n    expression: \"uint(this.size()) > rules.max_items ? 'value must contain no more than %s item(s)'.format([rules.max_items]) : ''\"\n  }];\n\n  // `unique` indicates that all elements in this field must\n  // be unique. This constraint is strictly applicable to scalar and enum\n  // types, with message types not being supported.\n  //\n  // ```proto\n  // message MyRepeated {\n  //   // repeated value must contain unique items\n  //   repeated string value = 1 [(buf.validate.field).repeated.unique = true];\n  // }\n  // ```\n  optional bool unique = 3 [(predefined).cel = {\n    id: \"repeated.unique\"\n    message: \"repeated value must contain unique items\"\n    expression: \"!rules.unique || this.unique()\"\n  }];\n\n  // `items` details the constraints to be applied to each item\n  // in the field. Even for repeated message fields, validation is executed\n  // against each item unless skip is explicitly specified.\n  //\n  // ```proto\n  // message MyRepeated {\n  //   // The items in the field `value` must follow the specified constraints.\n  //   repeated string value = 1 [(buf.validate.field).repeated.items = {\n  //     string: {\n  //       min_len: 3\n  //       max_len: 10\n  //     }\n  //   }];\n  // }\n  // ```\n  optional FieldConstraints items = 4;\n\n  // Extension fields in this range that have the (buf.validate.predefined)\n  // option set will be treated as predefined field constraints that can then be\n  // set on the field options of other fields to apply field constraints.\n  // Extension numbers 1000 to 99999 are reserved for extension numbers that are\n  // defined in the [Protobuf Global Extension Registry][1]. Extension numbers\n  // above this range are reserved for extension numbers that are not explicitly\n  // assigned. For rules defined in publicly-consumed schemas, use of extensions\n  // above 99999 is discouraged due to the risk of conflicts.\n  //\n  // [1]: https://github.com/protocolbuffers/protobuf/blob/main/docs/options.md\n  extensions 1000 to max;\n}\n\n// MapRules describe the constraints applied to `map` values.\nmessage MapRules {\n  //Specifies the minimum number of key-value pairs allowed. If the field has\n  // fewer key-value pairs than specified, an error message is generated.\n  //\n  // ```proto\n  // message MyMap {\n  //   // The field `value` must have at least 2 key-value pairs.\n  //   map<string, string> value = 1 [(buf.validate.field).map.min_pairs = 2];\n  // }\n  // ```\n  optional uint64 min_pairs = 1 [(predefined).cel = {\n    id: \"map.min_pairs\"\n    expression: \"uint(this.size()) < rules.min_pairs ? 'map must be at least %d entries'.format([rules.min_pairs]) : ''\"\n  }];\n\n  //Specifies the maximum number of key-value pairs allowed. If the field has\n  // more key-value pairs than specified, an error message is generated.\n  //\n  // ```proto\n  // message MyMap {\n  //   // The field `value` must have at most 3 key-value pairs.\n  //   map<string, string> value = 1 [(buf.validate.field).map.max_pairs = 3];\n  // }\n  // ```\n  optional uint64 max_pairs = 2 [(predefined).cel = {\n    id: \"map.max_pairs\"\n    expression: \"uint(this.size()) > rules.max_pairs ? 'map must be at most %d entries'.format([rules.max_pairs]) : ''\"\n  }];\n\n  //Specifies the constraints to be applied to each key in the field.\n  //\n  // ```proto\n  // message MyMap {\n  //   // The keys in the field `value` must follow the specified constraints.\n  //   map<string, string> value = 1 [(buf.validate.field).map.keys = {\n  //     string: {\n  //       min_len: 3\n  //       max_len: 10\n  //     }\n  //   }];\n  // }\n  // ```\n  optional FieldConstraints keys = 4;\n\n  //Specifies the constraints to be applied to the value of each key in the\n  // field. Message values will still have their validations evaluated unless\n  //skip is specified here.\n  //\n  // ```proto\n  // message MyMap {\n  //   // The values in the field `value` must follow the specified constraints.\n  //   map<string, string> value = 1 [(buf.validate.field).map.values = {\n  //     string: {\n  //       min_len: 5\n  //       max_len: 20\n  //     }\n  //   }];\n  // }\n  // ```\n  optional FieldConstraints values = 5;\n\n  // Extension fields in this range that have the (buf.validate.predefined)\n  // option set will be treated as predefined field constraints that can then be\n  // set on the field options of other fields to apply field constraints.\n  // Extension numbers 1000 to 99999 are reserved for extension numbers that are\n  // defined in the [Protobuf Global Extension Registry][1]. Extension numbers\n  // above this range are reserved for extension numbers that are not explicitly\n  // assigned. For rules defined in publicly-consumed schemas, use of extensions\n  // above 99999 is discouraged due to the risk of conflicts.\n  //\n  // [1]: https://github.com/protocolbuffers/protobuf/blob/main/docs/options.md\n  extensions 1000 to max;\n}\n\n// AnyRules describe constraints applied exclusively to the `google.protobuf.Any` well-known type.\nmessage AnyRules {\n  // `in` requires the field's `type_url` to be equal to one of the\n  //specified values. If it doesn't match any of the specified values, an error\n  // message is generated.\n  //\n  // ```proto\n  // message MyAny {\n  //   //  The `value` field must have a `type_url` equal to one of the specified values.\n  //   google.protobuf.Any value = 1 [(buf.validate.field).any.in = [\"type.googleapis.com/MyType1\", \"type.googleapis.com/MyType2\"]];\n  // }\n  // ```\n  repeated string in = 2;\n\n  // requires the field's type_url to be not equal to any of the specified values. If it matches any of the specified values, an error message is generated.\n  //\n  // ```proto\n  // message MyAny {\n  //   // The field `value` must not have a `type_url` equal to any of the specified values.\n  //   google.protobuf.Any value = 1 [(buf.validate.field).any.not_in = [\"type.googleapis.com/ForbiddenType1\", \"type.googleapis.com/ForbiddenType2\"]];\n  // }\n  // ```\n  repeated string not_in = 3;\n}\n\n// DurationRules describe the constraints applied exclusively to the `google.protobuf.Duration` well-known type.\nmessage DurationRules {\n  // `const` dictates that the field must match the specified value of the `google.protobuf.Duration` type exactly.\n  // If the field's value deviates from the specified value, an error message\n  // will be generated.\n  //\n  // ```proto\n  // message MyDuration {\n  //   // value must equal 5s\n  //   google.protobuf.Duration value = 1 [(buf.validate.field).duration.const = \"5s\"];\n  // }\n  // ```\n  optional google.protobuf.Duration const = 2 [(predefined).cel = {\n    id: \"duration.const\"\n    expression: \"this != rules.const ? 'value must equal %s'.format([rules.const]) : ''\"\n  }];\n  oneof less_than {\n    // `lt` stipulates that the field must be less than the specified value of the `google.protobuf.Duration` type,\n    // exclusive. If the field's value is greater than or equal to the specified\n    // value, an error message will be generated.\n    //\n    // ```proto\n    // message MyDuration {\n    //   // value must be less than 5s\n    //   google.protobuf.Duration value = 1 [(buf.validate.field).duration.lt = \"5s\"];\n    // }\n    // ```\n    google.protobuf.Duration lt = 3 [(predefined).cel = {\n      id: \"duration.lt\"\n      expression:\n          \"!has(rules.gte) && !has(rules.gt) && this >= rules.lt\"\n              \"? 'value must be less than %s'.format([rules.lt]) : ''\"\n    }];\n\n    // `lte` indicates that the field must be less than or equal to the specified\n    // value of the `google.protobuf.Duration` type, inclusive. If the field's value is greater than the specified value,\n    // an error message will be generated.\n    //\n    // ```proto\n    // message MyDuration {\n    //   // value must be less than or equal to 10s\n    //   google.protobuf.Duration value = 1 [(buf.validate.field).duration.lte = \"10s\"];\n    // }\n    // ```\n    google.protobuf.Duration lte = 4 [(predefined).cel = {\n      id: \"duration.lte\"\n      expression:\n          \"!has(rules.gte) && !has(rules.gt) && this > rules.lte\"\n              \"? 'value must be less than or equal to %s'.format([rules.lte]) : ''\"\n    }];\n  }\n  oneof greater_than {\n    // `gt` requires the duration field value to be greater than the specified\n    // value (exclusive). If the value of `gt` is larger than a specified `lt`\n    // or `lte`, the range is reversed, and the field value must be outside the\n    // specified range. If the field value doesn't meet the required conditions,\n    // an error message is generated.\n    //\n    // ```proto\n    // message MyDuration {\n    //   // duration must be greater than 5s [duration.gt]\n    //   google.protobuf.Duration value = 1 [(buf.validate.field).duration.gt = { seconds: 5 }];\n    //\n    //   // duration must be greater than 5s and less than 10s [duration.gt_lt]\n    //   google.protobuf.Duration another_value = 2 [(buf.validate.field).duration = { gt: { seconds: 5 }, lt: { seconds: 10 } }];\n    //\n    //   // duration must be greater than 10s or less than 5s [duration.gt_lt_exclusive]\n    //   google.protobuf.Duration other_value = 3 [(buf.validate.field).duration = { gt: { seconds: 10 }, lt: { seconds: 5 } }];\n    // }\n    // ```\n    google.protobuf.Duration gt = 5 [\n      (predefined).cel = {\n        id: \"duration.gt\"\n        expression:\n            \"!has(rules.lt) && !has(rules.lte) && this <= rules.gt\"\n                \"? 'value must be greater than %s'.format([rules.gt]) : ''\"\n      },\n      (predefined).cel = {\n        id: \"duration.gt_lt\"\n        expression:\n            \"has(rules.lt) && rules.lt >= rules.gt && (this >= rules.lt || this <= rules.gt)\"\n                \"? 'value must be greater than %s and less than %s'.format([rules.gt, rules.lt]) : ''\"\n      },\n      (predefined).cel = {\n        id: \"duration.gt_lt_exclusive\"\n        expression:\n            \"has(rules.lt) && rules.lt < rules.gt && (rules.lt <= this && this <= rules.gt)\"\n                \"? 'value must be greater than %s or less than %s'.format([rules.gt, rules.lt]) : ''\"\n      },\n      (predefined).cel = {\n        id: \"duration.gt_lte\"\n        expression:\n            \"has(rules.lte) && rules.lte >= rules.gt && (this > rules.lte || this <= rules.gt)\"\n                \"? 'value must be greater than %s and less than or equal to %s'.format([rules.gt, rules.lte]) : ''\"\n      },\n      (predefined).cel = {\n        id: \"duration.gt_lte_exclusive\"\n        expression:\n            \"has(rules.lte) && rules.lte < rules.gt && (rules.lte < this && this <= rules.gt)\"\n                \"? 'value must be greater than %s or less than or equal to %s'.format([rules.gt, rules.lte]) : ''\"\n      }\n    ];\n\n    // `gte` requires the duration field value to be greater than or equal to the\n    // specified value (exclusive). If the value of `gte` is larger than a\n    // specified `lt` or `lte`, the range is reversed, and the field value must\n    // be outside the specified range. If the field value doesn't meet the\n    // required conditions, an error message is generated.\n    //\n    // ```proto\n    // message MyDuration {\n    //  // duration must be greater than or equal to 5s [duration.gte]\n    //  google.protobuf.Duration value = 1 [(buf.validate.field).duration.gte = { seconds: 5 }];\n    //\n    //  // duration must be greater than or equal to 5s and less than 10s [duration.gte_lt]\n    //  google.protobuf.Duration another_value = 2 [(buf.validate.field).duration = { gte: { seconds: 5 }, lt: { seconds: 10 } }];\n    //\n    //  // duration must be greater than or equal to 10s or less than 5s [duration.gte_lt_exclusive]\n    //  google.protobuf.Duration other_value = 3 [(buf.validate.field).duration = { gte: { seconds: 10 }, lt: { seconds: 5 } }];\n    // }\n    // ```\n    google.protobuf.Duration gte = 6 [\n      (predefined).cel = {\n        id: \"duration.gte\"\n        expression:\n            \"!has(rules.lt) && !has(rules.lte) && this < rules.gte\"\n                \"? 'value must be greater than or equal to %s'.format([rules.gte]) : ''\"\n      },\n      (predefined).cel = {\n        id: \"duration.gte_lt\"\n        expression:\n            \"has(rules.lt) && rules.lt >= rules.gte && (this >= rules.lt || this < rules.gte)\"\n                \"? 'value must be greater than or equal to %s and less than %s'.format([rules.gte, rules.lt]) : ''\"\n      },\n      (predefined).cel = {\n        id: \"duration.gte_lt_exclusive\"\n        expression:\n            \"has(rules.lt) && rules.lt < rules.gte && (rules.lt <= this && this < rules.gte)\"\n                \"? 'value must be greater than or equal to %s or less than %s'.format([rules.gte, rules.lt]) : ''\"\n      },\n      (predefined).cel = {\n        id: \"duration.gte_lte\"\n        expression:\n            \"has(rules.lte) && rules.lte >= rules.gte && (this > rules.lte || this < rules.gte)\"\n                \"? 'value must be greater than or equal to %s and less than or equal to %s'.format([rules.gte, rules.lte]) : ''\"\n      },\n      (predefined).cel = {\n        id: \"duration.gte_lte_exclusive\"\n        expression:\n            \"has(rules.lte) && rules.lte < rules.gte && (rules.lte < this && this < rules.gte)\"\n                \"? 'value must be greater than or equal to %s or less than or equal to %s'.format([rules.gte, rules.lte]) : ''\"\n      }\n    ];\n  }\n\n  // `in` asserts that the field must be equal to one of the specified values of the `google.protobuf.Duration` type.\n  // If the field's value doesn't correspond to any of the specified values,\n  // an error message will be generated.\n  //\n  // ```proto\n  // message MyDuration {\n  //   // value must be in list [1s, 2s, 3s]\n  //   google.protobuf.Duration value = 1 [(buf.validate.field).duration.in = [\"1s\", \"2s\", \"3s\"]];\n  // }\n  // ```\n  repeated google.protobuf.Duration in = 7 [(predefined).cel = {\n    id: \"duration.in\"\n    expression: \"!(this in dyn(rules)['in']) ? 'value must be in list %s'.format([dyn(rules)['in']]) : ''\"\n  }];\n\n  // `not_in` denotes that the field must not be equal to\n  // any of the specified values of the `google.protobuf.Duration` type.\n  // If the field's value matches any of these values, an error message will be\n  // generated.\n  //\n  // ```proto\n  // message MyDuration {\n  //   // value must not be in list [1s, 2s, 3s]\n  //   google.protobuf.Duration value = 1 [(buf.validate.field).duration.not_in = [\"1s\", \"2s\", \"3s\"]];\n  // }\n  // ```\n  repeated google.protobuf.Duration not_in = 8 [(predefined).cel = {\n    id: \"duration.not_in\"\n    expression: \"this in rules.not_in ? 'value must not be in list %s'.format([rules.not_in]) : ''\"\n  }];\n\n  // `example` specifies values that the field may have. These values SHOULD\n  // conform to other constraints. `example` values will not impact validation\n  // but may be used as helpful guidance on how to populate the given field.\n  //\n  // ```proto\n  // message MyDuration {\n  //   google.protobuf.Duration value = 1 [\n  //     (buf.validate.field).duration.example = { seconds: 1 },\n  //     (buf.validate.field).duration.example = { seconds: 2 },\n  //   ];\n  // }\n  // ```\n  repeated google.protobuf.Duration example = 9 [(predefined).cel = {\n    id: \"duration.example\"\n    expression: \"true\"\n  }];\n\n  // Extension fields in this range that have the (buf.validate.predefined)\n  // option set will be treated as predefined field constraints that can then be\n  // set on the field options of other fields to apply field constraints.\n  // Extension numbers 1000 to 99999 are reserved for extension numbers that are\n  // defined in the [Protobuf Global Extension Registry][1]. Extension numbers\n  // above this range are reserved for extension numbers that are not explicitly\n  // assigned. For rules defined in publicly-consumed schemas, use of extensions\n  // above 99999 is discouraged due to the risk of conflicts.\n  //\n  // [1]: https://github.com/protocolbuffers/protobuf/blob/main/docs/options.md\n  extensions 1000 to max;\n}\n\n// TimestampRules describe the constraints applied exclusively to the `google.protobuf.Timestamp` well-known type.\nmessage TimestampRules {\n  // `const` dictates that this field, of the `google.protobuf.Timestamp` type, must exactly match the specified value. If the field value doesn't correspond to the specified timestamp, an error message will be generated.\n  //\n  // ```proto\n  // message MyTimestamp {\n  //   // value must equal 2023-05-03T10:00:00Z\n  //   google.protobuf.Timestamp created_at = 1 [(buf.validate.field).timestamp.const = {seconds: 1727998800}];\n  // }\n  // ```\n  optional google.protobuf.Timestamp const = 2 [(predefined).cel = {\n    id: \"timestamp.const\"\n    expression: \"this != rules.const ? 'value must equal %s'.format([rules.const]) : ''\"\n  }];\n  oneof less_than {\n    // requires the duration field value to be less than the specified value (field < value). If the field value doesn't meet the required conditions, an error message is generated.\n    //\n    // ```proto\n    // message MyDuration {\n    //   // duration must be less than 'P3D' [duration.lt]\n    //   google.protobuf.Duration value = 1 [(buf.validate.field).duration.lt = { seconds: 259200 }];\n    // }\n    // ```\n    google.protobuf.Timestamp lt = 3 [(predefined).cel = {\n      id: \"timestamp.lt\"\n      expression:\n          \"!has(rules.gte) && !has(rules.gt) && this >= rules.lt\"\n              \"? 'value must be less than %s'.format([rules.lt]) : ''\"\n    }];\n\n    // requires the timestamp field value to be less than or equal to the specified value (field <= value). If the field value doesn't meet the required conditions, an error message is generated.\n    //\n    // ```proto\n    // message MyTimestamp {\n    //   // timestamp must be less than or equal to '2023-05-14T00:00:00Z' [timestamp.lte]\n    //   google.protobuf.Timestamp value = 1 [(buf.validate.field).timestamp.lte = { seconds: 1678867200 }];\n    // }\n    // ```\n    google.protobuf.Timestamp lte = 4 [(predefined).cel = {\n      id: \"timestamp.lte\"\n      expression:\n          \"!has(rules.gte) && !has(rules.gt) && this > rules.lte\"\n              \"? 'value must be less than or equal to %s'.format([rules.lte]) : ''\"\n    }];\n\n    // `lt_now` specifies that this field, of the `google.protobuf.Timestamp` type, must be less than the current time. `lt_now` can only be used with the `within` rule.\n    //\n    // ```proto\n    // message MyTimestamp {\n    //  // value must be less than now\n    //   google.protobuf.Timestamp created_at = 1 [(buf.validate.field).timestamp.lt_now = true];\n    // }\n    // ```\n    bool lt_now = 7 [(predefined).cel = {\n      id: \"timestamp.lt_now\"\n      expression: \"(rules.lt_now && this > now) ? 'value must be less than now' : ''\"\n    }];\n  }\n  oneof greater_than {\n    // `gt` requires the timestamp field value to be greater than the specified\n    // value (exclusive). If the value of `gt` is larger than a specified `lt`\n    // or `lte`, the range is reversed, and the field value must be outside the\n    // specified range. If the field value doesn't meet the required conditions,\n    // an error message is generated.\n    //\n    // ```proto\n    // message MyTimestamp {\n    //   // timestamp must be greater than '2023-01-01T00:00:00Z' [timestamp.gt]\n    //   google.protobuf.Timestamp value = 1 [(buf.validate.field).timestamp.gt = { seconds: 1672444800 }];\n    //\n    //   // timestamp must be greater than '2023-01-01T00:00:00Z' and less than '2023-01-02T00:00:00Z' [timestamp.gt_lt]\n    //   google.protobuf.Timestamp another_value = 2 [(buf.validate.field).timestamp = { gt: { seconds: 1672444800 }, lt: { seconds: 1672531200 } }];\n    //\n    //   // timestamp must be greater than '2023-01-02T00:00:00Z' or less than '2023-01-01T00:00:00Z' [timestamp.gt_lt_exclusive]\n    //   google.protobuf.Timestamp other_value = 3 [(buf.validate.field).timestamp = { gt: { seconds: 1672531200 }, lt: { seconds: 1672444800 } }];\n    // }\n    // ```\n    google.protobuf.Timestamp gt = 5 [\n      (predefined).cel = {\n        id: \"timestamp.gt\"\n        expression:\n            \"!has(rules.lt) && !has(rules.lte) && this <= rules.gt\"\n                \"? 'value must be greater than %s'.format([rules.gt]) : ''\"\n      },\n      (predefined).cel = {\n        id: \"timestamp.gt_lt\"\n        expression:\n            \"has(rules.lt) && rules.lt >= rules.gt && (this >= rules.lt || this <= rules.gt)\"\n                \"? 'value must be greater than %s and less than %s'.format([rules.gt, rules.lt]) : ''\"\n      },\n      (predefined).cel = {\n        id: \"timestamp.gt_lt_exclusive\"\n        expression:\n            \"has(rules.lt) && rules.lt < rules.gt && (rules.lt <= this && this <= rules.gt)\"\n                \"? 'value must be greater than %s or less than %s'.format([rules.gt, rules.lt]) : ''\"\n      },\n      (predefined).cel = {\n        id: \"timestamp.gt_lte\"\n        expression:\n            \"has(rules.lte) && rules.lte >= rules.gt && (this > rules.lte || this <= rules.gt)\"\n                \"? 'value must be greater than %s and less than or equal to %s'.format([rules.gt, rules.lte]) : ''\"\n      },\n      (predefined).cel = {\n        id: \"timestamp.gt_lte_exclusive\"\n        expression:\n            \"has(rules.lte) && rules.lte < rules.gt && (rules.lte < this && this <= rules.gt)\"\n                \"? 'value must be greater than %s or less than or equal to %s'.format([rules.gt, rules.lte]) : ''\"\n      }\n    ];\n\n    // `gte` requires the timestamp field value to be greater than or equal to the\n    // specified value (exclusive). If the value of `gte` is larger than a\n    // specified `lt` or `lte`, the range is reversed, and the field value\n    // must be outside the specified range. If the field value doesn't meet\n    // the required conditions, an error message is generated.\n    //\n    // ```proto\n    // message MyTimestamp {\n    //   // timestamp must be greater than or equal to '2023-01-01T00:00:00Z' [timestamp.gte]\n    //   google.protobuf.Timestamp value = 1 [(buf.validate.field).timestamp.gte = { seconds: 1672444800 }];\n    //\n    //   // timestamp must be greater than or equal to '2023-01-01T00:00:00Z' and less than '2023-01-02T00:00:00Z' [timestamp.gte_lt]\n    //   google.protobuf.Timestamp another_value = 2 [(buf.validate.field).timestamp = { gte: { seconds: 1672444800 }, lt: { seconds: 1672531200 } }];\n    //\n    //   // timestamp must be greater than or equal to '2023-01-02T00:00:00Z' or less than '2023-01-01T00:00:00Z' [timestamp.gte_lt_exclusive]\n    //   google.protobuf.Timestamp other_value = 3 [(buf.validate.field).timestamp = { gte: { seconds: 1672531200 }, lt: { seconds: 1672444800 } }];\n    // }\n    // ```\n    google.protobuf.Timestamp gte = 6 [\n      (predefined).cel = {\n        id: \"timestamp.gte\"\n        expression:\n            \"!has(rules.lt) && !has(rules.lte) && this < rules.gte\"\n                \"? 'value must be greater than or equal to %s'.format([rules.gte]) : ''\"\n      },\n      (predefined).cel = {\n        id: \"timestamp.gte_lt\"\n        expression:\n            \"has(rules.lt) && rules.lt >= rules.gte && (this >= rules.lt || this < rules.gte)\"\n                \"? 'value must be greater than or equal to %s and less than %s'.format([rules.gte, rules.lt]) : ''\"\n      },\n      (predefined).cel = {\n        id: \"timestamp.gte_lt_exclusive\"\n        expression:\n            \"has(rules.lt) && rules.lt < rules.gte && (rules.lt <= this && this < rules.gte)\"\n                \"? 'value must be greater than or equal to %s or less than %s'.format([rules.gte, rules.lt]) : ''\"\n      },\n      (predefined).cel = {\n        id: \"timestamp.gte_lte\"\n        expression:\n            \"has(rules.lte) && rules.lte >= rules.gte && (this > rules.lte || this < rules.gte)\"\n                \"? 'value must be greater than or equal to %s and less than or equal to %s'.format([rules.gte, rules.lte]) : ''\"\n      },\n      (predefined).cel = {\n        id: \"timestamp.gte_lte_exclusive\"\n        expression:\n            \"has(rules.lte) && rules.lte < rules.gte && (rules.lte < this && this < rules.gte)\"\n                \"? 'value must be greater than or equal to %s or less than or equal to %s'.format([rules.gte, rules.lte]) : ''\"\n      }\n    ];\n\n    // `gt_now` specifies that this field, of the `google.protobuf.Timestamp` type, must be greater than the current time. `gt_now` can only be used with the `within` rule.\n    //\n    // ```proto\n    // message MyTimestamp {\n    //   // value must be greater than now\n    //   google.protobuf.Timestamp created_at = 1 [(buf.validate.field).timestamp.gt_now = true];\n    // }\n    // ```\n    bool gt_now = 8 [(predefined).cel = {\n      id: \"timestamp.gt_now\"\n      expression: \"(rules.gt_now && this < now) ? 'value must be greater than now' : ''\"\n    }];\n  }\n\n  // `within` specifies that this field, of the `google.protobuf.Timestamp` type, must be within the specified duration of the current time. If the field value isn't within the duration, an error message is generated.\n  //\n  // ```proto\n  // message MyTimestamp {\n  //   // value must be within 1 hour of now\n  //   google.protobuf.Timestamp created_at = 1 [(buf.validate.field).timestamp.within = {seconds: 3600}];\n  // }\n  // ```\n  optional google.protobuf.Duration within = 9 [(predefined).cel = {\n    id: \"timestamp.within\"\n    expression: \"this < now-rules.within || this > now+rules.within ? 'value must be within %s of now'.format([rules.within]) : ''\"\n  }];\n\n  // `example` specifies values that the field may have. These values SHOULD\n  // conform to other constraints. `example` values will not impact validation\n  // but may be used as helpful guidance on how to populate the given field.\n  //\n  // ```proto\n  // message MyTimestamp {\n  //   google.protobuf.Timestamp value = 1 [\n  //     (buf.validate.field).timestamp.example = { seconds: 1672444800 },\n  //     (buf.validate.field).timestamp.example = { seconds: 1672531200 },\n  //   ];\n  // }\n  // ```\n\n  repeated google.protobuf.Timestamp example = 10 [(predefined).cel = {\n    id: \"timestamp.example\"\n    expression: \"true\"\n  }];\n\n  // Extension fields in this range that have the (buf.validate.predefined)\n  // option set will be treated as predefined field constraints that can then be\n  // set on the field options of other fields to apply field constraints.\n  // Extension numbers 1000 to 99999 are reserved for extension numbers that are\n  // defined in the [Protobuf Global Extension Registry][1]. Extension numbers\n  // above this range are reserved for extension numbers that are not explicitly\n  // assigned. For rules defined in publicly-consumed schemas, use of extensions\n  // above 99999 is discouraged due to the risk of conflicts.\n  //\n  // [1]: https://github.com/protocolbuffers/protobuf/blob/main/docs/options.md\n  extensions 1000 to max;\n}\n\n// `Violations` is a collection of `Violation` messages. This message type is returned by\n// protovalidate when a proto message fails to meet the requirements set by the `Constraint` validation rules.\n// Each individual violation is represented by a `Violation` message.\nmessage Violations {\n  // `violations` is a repeated field that contains all the `Violation` messages corresponding to the violations detected.\n  repeated Violation violations = 1;\n}\n\n// `Violation` represents a single instance where a validation rule, expressed\n// as a `Constraint`, was not met. It provides information about the field that\n// caused the violation, the specific constraint that wasn't fulfilled, and a\n// human-readable error message.\n//\n// ```json\n// {\n//   \"fieldPath\": \"bar\",\n//   \"constraintId\": \"foo.bar\",\n//   \"message\": \"bar must be greater than 0\"\n// }\n// ```\nmessage Violation {\n  // `field` is a machine-readable path to the field that failed validation.\n  // This could be a nested field, in which case the path will include all the parent fields leading to the actual field that caused the violation.\n  //\n  // For example, consider the following message:\n  //\n  // ```proto\n  // message Message {\n  //   bool a = 1 [(buf.validate.field).required = true];\n  // }\n  // ```\n  //\n  // It could produce the following violation:\n  //\n  // ```textproto\n  // violation {\n  //   field { element { field_number: 1, field_name: \"a\", field_type: 8 } }\n  //   ...\n  // }\n  // ```\n  optional FieldPath field = 5;\n\n  // `rule` is a machine-readable path that points to the specific constraint rule that failed validation.\n  // This will be a nested field starting from the FieldConstraints of the field that failed validation.\n  // For custom constraints, this will provide the path of the constraint, e.g. `cel[0]`.\n  //\n  // For example, consider the following message:\n  //\n  // ```proto\n  // message Message {\n  //   bool a = 1 [(buf.validate.field).required = true];\n  //   bool b = 2 [(buf.validate.field).cel = {\n  //     id: \"custom_constraint\",\n  //     expression: \"!this ? 'b must be true': ''\"\n  //   }]\n  // }\n  // ```\n  //\n  // It could produce the following violations:\n  //\n  // ```textproto\n  // violation {\n  //   rule { element { field_number: 25, field_name: \"required\", field_type: 8 } }\n  //   ...\n  // }\n  // violation {\n  //   rule { element { field_number: 23, field_name: \"cel\", field_type: 11, index: 0 } }\n  //   ...\n  // }\n  // ```\n  optional FieldPath rule = 6;\n\n  // `field_path` is a human-readable identifier that points to the specific field that failed the validation.\n  // This could be a nested field, in which case the path will include all the parent fields leading to the actual field that caused the violation.\n  //\n  // Deprecated: use the `field` instead.\n  optional string field_path = 1 [deprecated = true];\n\n  // `constraint_id` is the unique identifier of the `Constraint` that was not fulfilled.\n  // This is the same `id` that was specified in the `Constraint` message, allowing easy tracing of which rule was violated.\n  optional string constraint_id = 2;\n\n  // `message` is a human-readable error message that describes the nature of the violation.\n  // This can be the default error message from the violated `Constraint`, or it can be a custom message that gives more context about the violation.\n  optional string message = 3;\n\n  // `for_key` indicates whether the violation was caused by a map key, rather than a value.\n  optional bool for_key = 4;\n}\n\n// `FieldPath` provides a path to a nested protobuf field.\n//\n// This message provides enough information to render a dotted field path even without protobuf descriptors.\n// It also provides enough information to resolve a nested field through unknown wire data.\nmessage FieldPath {\n  // `elements` contains each element of the path, starting from the root and recursing downward.\n  repeated FieldPathElement elements = 1;\n}\n\n// `FieldPathElement` provides enough information to nest through a single protobuf field.\n//\n// If the selected field is a map or repeated field, the `subscript` value selects a specific element from it.\n// A path that refers to a value nested under a map key or repeated field index will have a `subscript` value.\n// The `field_type` field allows unambiguous resolution of a field even if descriptors are not available.\nmessage FieldPathElement {\n  // `field_number` is the field number this path element refers to.\n  optional int32 field_number = 1;\n\n  // `field_name` contains the field name this path element refers to.\n  // This can be used to display a human-readable path even if the field number is unknown.\n  optional string field_name = 2;\n\n  // `field_type` specifies the type of this field. When using reflection, this value is not needed.\n  //\n  // This value is provided to make it possible to traverse unknown fields through wire data.\n  // When traversing wire data, be mindful of both packed[1] and delimited[2] encoding schemes.\n  //\n  // [1]: https://protobuf.dev/programming-guides/encoding/#packed\n  // [2]: https://protobuf.dev/programming-guides/encoding/#groups\n  //\n  // N.B.: Although groups are deprecated, the corresponding delimited encoding scheme is not, and\n  // can be explicitly used in Protocol Buffers 2023 Edition.\n  optional google.protobuf.FieldDescriptorProto.Type field_type = 3;\n\n  // `key_type` specifies the map key type of this field. This value is useful when traversing\n  // unknown fields through wire data: specifically, it allows handling the differences between\n  // different integer encodings.\n  optional google.protobuf.FieldDescriptorProto.Type key_type = 4;\n\n  // `value_type` specifies map value type of this field. This is useful if you want to display a\n  // value inside unknown fields through wire data.\n  optional google.protobuf.FieldDescriptorProto.Type value_type = 5;\n\n  // `subscript` contains a repeated index or map key, if this path element nests into a repeated or map field.\n  oneof subscript {\n    // `index` specifies a 0-based index into a repeated field.\n    uint64 index = 6;\n\n    // `bool_key` specifies a map key of type bool.\n    bool bool_key = 7;\n\n    // `int_key` specifies a map key of type int32, int64, sint32, sint64, sfixed32 or sfixed64.\n    int64 int_key = 8;\n\n    // `uint_key` specifies a map key of type uint32, uint64, fixed32 or fixed64.\n    uint64 uint_key = 9;\n\n    // `string_key` specifies a map key of type string.\n    string string_key = 10;\n  }\n}"
  },
  {
    "path": "third_party/buf.yaml",
    "content": "version: v1\nname: buf.build/kratos/apis\nbreaking:\n  use:\n    - FILE\nlint:\n  use:\n    - DEFAULT\nbuild:\n  excludes:\n    - google\n    - validate\n"
  },
  {
    "path": "third_party/errors/errors.proto",
    "content": "syntax = \"proto3\";\n\npackage errors;\n\noption go_package = \"github.com/go-kratos/kratos/v2/errors;errors\";\noption java_multiple_files = true;\noption java_package = \"com.github.kratos.errors\";\noption objc_class_prefix = \"KratosErrors\";\n\nimport \"google/protobuf/descriptor.proto\";\n\nextend google.protobuf.EnumOptions {\n  int32 default_code = 1108;\n}\n\nextend google.protobuf.EnumValueOptions {\n  int32 code = 1109;\n}\n"
  },
  {
    "path": "third_party/google/api/annotations.proto",
    "content": "// Copyright (c) 2015, Google Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nsyntax = \"proto3\";\n\npackage google.api;\n\nimport \"google/api/http.proto\";\nimport \"google/protobuf/descriptor.proto\";\n\noption go_package = \"google.golang.org/genproto/googleapis/api/annotations;annotations\";\noption java_multiple_files = true;\noption java_outer_classname = \"AnnotationsProto\";\noption java_package = \"com.google.api\";\noption objc_class_prefix = \"GAPI\";\n\nextend google.protobuf.MethodOptions {\n  // See `HttpRule`.\n  HttpRule http = 72295728;\n}\n"
  },
  {
    "path": "third_party/google/api/client.proto",
    "content": "// Copyright 2019 Google LLC.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nsyntax = \"proto3\";\n\npackage google.api;\n\nimport \"google/protobuf/descriptor.proto\";\n\noption go_package = \"google.golang.org/genproto/googleapis/api/annotations;annotations\";\noption java_multiple_files = true;\noption java_outer_classname = \"ClientProto\";\noption java_package = \"com.google.api\";\noption objc_class_prefix = \"GAPI\";\n\n\nextend google.protobuf.ServiceOptions {\n  // The hostname for this service.\n  // This should be specified with no prefix or protocol.\n  //\n  // Example:\n  //\n  //   service Foo {\n  //     option (google.api.default_host) = \"foo.googleapi.com\";\n  //     ...\n  //   }\n  string default_host = 1049;\n\n  // OAuth scopes needed for the client.\n  //\n  // Example:\n  //\n  //   service Foo {\n  //     option (google.api.oauth_scopes) = \\\n  //       \"https://www.googleapis.com/auth/cloud-platform\";\n  //     ...\n  //   }\n  //\n  // If there is more than one scope, use a comma-separated string:\n  //\n  // Example:\n  //\n  //   service Foo {\n  //     option (google.api.oauth_scopes) = \\\n  //       \"https://www.googleapis.com/auth/cloud-platform,\"\n  //       \"https://www.googleapis.com/auth/monitoring\";\n  //     ...\n  //   }\n  string oauth_scopes = 1050;\n}\n\n\nextend google.protobuf.MethodOptions {\n  // A definition of a client library method signature.\n  //\n  // In client libraries, each proto RPC corresponds to one or more methods\n  // which the end user is able to call, and calls the underlying RPC.\n  // Normally, this method receives a single argument (a struct or instance\n  // corresponding to the RPC request object). Defining this field will\n  // add one or more overloads providing flattened or simpler method signatures\n  // in some languages.\n  //\n  // The fields on the method signature are provided as a comma-separated\n  // string.\n  //\n  // For example, the proto RPC and annotation:\n  //\n  //   rpc CreateSubscription(CreateSubscriptionRequest)\n  //       returns (Subscription) {\n  //     option (google.api.method_signature) = \"name,topic\";\n  //   }\n  //\n  // Would add the following Java overload (in addition to the method accepting\n  // the request object):\n  //\n  //   public final Subscription createSubscription(String name, String topic)\n  //\n  // The following backwards-compatibility guidelines apply:\n  //\n  //   * Adding this annotation to an unannotated method is backwards\n  //     compatible.\n  //   * Adding this annotation to a method which already has existing\n  //     method signature annotations is backwards compatible if and only if\n  //     the new method signature annotation is last in the sequence.\n  //   * Modifying or removing an existing method signature annotation is\n  //     a breaking change.\n  //   * Re-ordering existing method signature annotations is a breaking\n  //     change.\n  repeated string method_signature = 1051;\n}"
  },
  {
    "path": "third_party/google/api/field_behavior.proto",
    "content": "// Copyright 2019 Google LLC.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nsyntax = \"proto3\";\n\npackage google.api;\n\nimport \"google/protobuf/descriptor.proto\";\n\noption go_package = \"google.golang.org/genproto/googleapis/api/annotations;annotations\";\noption java_multiple_files = true;\noption java_outer_classname = \"FieldBehaviorProto\";\noption java_package = \"com.google.api\";\noption objc_class_prefix = \"GAPI\";\n\n\n// An indicator of the behavior of a given field (for example, that a field\n// is required in requests, or given as output but ignored as input).\n// This **does not** change the behavior in protocol buffers itself; it only\n// denotes the behavior and may affect how API tooling handles the field.\n//\n// Note: This enum **may** receive new values in the future.\nenum FieldBehavior {\n  // Conventional default for enums. Do not use this.\n  FIELD_BEHAVIOR_UNSPECIFIED = 0;\n\n  // Specifically denotes a field as optional.\n  // While all fields in protocol buffers are optional, this may be specified\n  // for emphasis if appropriate.\n  OPTIONAL = 1;\n\n  // Denotes a field as required.\n  // This indicates that the field **must** be provided as part of the request,\n  // and failure to do so will cause an error (usually `INVALID_ARGUMENT`).\n  REQUIRED = 2;\n\n  // Denotes a field as output only.\n  // This indicates that the field is provided in responses, but including the\n  // field in a request does nothing (the server *must* ignore it and\n  // *must not* throw an error as a result of the field's presence).\n  OUTPUT_ONLY = 3;\n\n  // Denotes a field as input only.\n  // This indicates that the field is provided in requests, and the\n  // corresponding field is not included in output.\n  INPUT_ONLY = 4;\n\n  // Denotes a field as immutable.\n  // This indicates that the field may be set once in a request to create a\n  // resource, but may not be changed thereafter.\n  IMMUTABLE = 5;\n}\n\n\nextend google.protobuf.FieldOptions {\n  // A designation of a specific field behavior (required, output only, etc.)\n  // in protobuf messages.\n  //\n  // Examples:\n  //\n  //   string name = 1 [(google.api.field_behavior) = REQUIRED];\n  //   State state = 1 [(google.api.field_behavior) = OUTPUT_ONLY];\n  //   google.protobuf.Duration ttl = 1\n  //     [(google.api.field_behavior) = INPUT_ONLY];\n  //   google.protobuf.Timestamp expire_time = 1\n  //     [(google.api.field_behavior) = OUTPUT_ONLY,\n  //      (google.api.field_behavior) = IMMUTABLE];\n  repeated FieldBehavior field_behavior = 1052;\n}"
  },
  {
    "path": "third_party/google/api/http.proto",
    "content": "// Copyright 2020 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nsyntax = \"proto3\";\n\npackage google.api;\n\noption cc_enable_arenas = true;\noption go_package = \"google.golang.org/genproto/googleapis/api/annotations;annotations\";\noption java_multiple_files = true;\noption java_outer_classname = \"HttpProto\";\noption java_package = \"com.google.api\";\noption objc_class_prefix = \"GAPI\";\n\n// Defines the HTTP configuration for an API service. It contains a list of\n// [HttpRule][google.api.HttpRule], each specifying the mapping of an RPC method\n// to one or more HTTP REST API methods.\nmessage Http {\n  // A list of HTTP configuration rules that apply to individual API methods.\n  //\n  // **NOTE:** All service configuration rules follow \"last one wins\" order.\n  repeated HttpRule rules = 1;\n\n  // When set to true, URL path parameters will be fully URI-decoded except in\n  // cases of single segment matches in reserved expansion, where \"%2F\" will be\n  // left encoded.\n  //\n  // The default behavior is to not decode RFC 6570 reserved characters in multi\n  // segment matches.\n  bool fully_decode_reserved_expansion = 2;\n}\n\n// # gRPC Transcoding\n//\n// gRPC Transcoding is a feature for mapping between a gRPC method and one or\n// more HTTP REST endpoints. It allows developers to build a single API service\n// that supports both gRPC APIs and REST APIs. Many systems, including [Google\n// APIs](https://github.com/googleapis/googleapis),\n// [Cloud Endpoints](https://cloud.google.com/endpoints), [gRPC\n// Gateway](https://github.com/grpc-ecosystem/grpc-gateway),\n// and [Envoy](https://github.com/envoyproxy/envoy) proxy support this feature\n// and use it for large scale production services.\n//\n// `HttpRule` defines the schema of the gRPC/REST mapping. The mapping specifies\n// how different portions of the gRPC request message are mapped to the URL\n// path, URL query parameters, and HTTP request body. It also controls how the\n// gRPC response message is mapped to the HTTP response body. `HttpRule` is\n// typically specified as an `google.api.http` annotation on the gRPC method.\n//\n// Each mapping specifies a URL path template and an HTTP method. The path\n// template may refer to one or more fields in the gRPC request message, as long\n// as each field is a non-repeated field with a primitive (non-message) type.\n// The path template controls how fields of the request message are mapped to\n// the URL path.\n//\n// Example:\n//\n//     service Messaging {\n//       rpc GetMessage(GetMessageRequest) returns (Message) {\n//         option (google.api.http) = {\n//             get: \"/v1/{name=messages/*}\"\n//         };\n//       }\n//     }\n//     message GetMessageRequest {\n//       string name = 1; // Mapped to URL path.\n//     }\n//     message Message {\n//       string text = 1; // The resource content.\n//     }\n//\n// This enables an HTTP REST to gRPC mapping as below:\n//\n// HTTP | gRPC\n// -----|-----\n// `GET /v1/messages/123456`  | `GetMessage(name: \"messages/123456\")`\n//\n// Any fields in the request message which are not bound by the path template\n// automatically become HTTP query parameters if there is no HTTP request body.\n// For example:\n//\n//     service Messaging {\n//       rpc GetMessage(GetMessageRequest) returns (Message) {\n//         option (google.api.http) = {\n//             get:\"/v1/messages/{message_id}\"\n//         };\n//       }\n//     }\n//     message GetMessageRequest {\n//       message SubMessage {\n//         string subfield = 1;\n//       }\n//       string message_id = 1; // Mapped to URL path.\n//       int64 revision = 2;    // Mapped to URL query parameter `revision`.\n//       SubMessage sub = 3;    // Mapped to URL query parameter `sub.subfield`.\n//     }\n//\n// This enables a HTTP JSON to RPC mapping as below:\n//\n// HTTP | gRPC\n// -----|-----\n// `GET /v1/messages/123456?revision=2&sub.subfield=foo` |\n// `GetMessage(message_id: \"123456\" revision: 2 sub: SubMessage(subfield:\n// \"foo\"))`\n//\n// Note that fields which are mapped to URL query parameters must have a\n// primitive type or a repeated primitive type or a non-repeated message type.\n// In the case of a repeated type, the parameter can be repeated in the URL\n// as `...?param=A&param=B`. In the case of a message type, each field of the\n// message is mapped to a separate parameter, such as\n// `...?foo.a=A&foo.b=B&foo.c=C`.\n//\n// For HTTP methods that allow a request body, the `body` field\n// specifies the mapping. Consider a REST update method on the\n// message resource collection:\n//\n//     service Messaging {\n//       rpc UpdateMessage(UpdateMessageRequest) returns (Message) {\n//         option (google.api.http) = {\n//           patch: \"/v1/messages/{message_id}\"\n//           body: \"message\"\n//         };\n//       }\n//     }\n//     message UpdateMessageRequest {\n//       string message_id = 1; // mapped to the URL\n//       Message message = 2;   // mapped to the body\n//     }\n//\n// The following HTTP JSON to RPC mapping is enabled, where the\n// representation of the JSON in the request body is determined by\n// protos JSON encoding:\n//\n// HTTP | gRPC\n// -----|-----\n// `PATCH /v1/messages/123456 { \"text\": \"Hi!\" }` | `UpdateMessage(message_id:\n// \"123456\" message { text: \"Hi!\" })`\n//\n// The special name `*` can be used in the body mapping to define that\n// every field not bound by the path template should be mapped to the\n// request body.  This enables the following alternative definition of\n// the update method:\n//\n//     service Messaging {\n//       rpc UpdateMessage(Message) returns (Message) {\n//         option (google.api.http) = {\n//           patch: \"/v1/messages/{message_id}\"\n//           body: \"*\"\n//         };\n//       }\n//     }\n//     message Message {\n//       string message_id = 1;\n//       string text = 2;\n//     }\n//\n//\n// The following HTTP JSON to RPC mapping is enabled:\n//\n// HTTP | gRPC\n// -----|-----\n// `PATCH /v1/messages/123456 { \"text\": \"Hi!\" }` | `UpdateMessage(message_id:\n// \"123456\" text: \"Hi!\")`\n//\n// Note that when using `*` in the body mapping, it is not possible to\n// have HTTP parameters, as all fields not bound by the path end in\n// the body. This makes this option more rarely used in practice when\n// defining REST APIs. The common usage of `*` is in custom methods\n// which don't use the URL at all for transferring data.\n//\n// It is possible to define multiple HTTP methods for one RPC by using\n// the `additional_bindings` option. Example:\n//\n//     service Messaging {\n//       rpc GetMessage(GetMessageRequest) returns (Message) {\n//         option (google.api.http) = {\n//           get: \"/v1/messages/{message_id}\"\n//           additional_bindings {\n//             get: \"/v1/users/{user_id}/messages/{message_id}\"\n//           }\n//         };\n//       }\n//     }\n//     message GetMessageRequest {\n//       string message_id = 1;\n//       string user_id = 2;\n//     }\n//\n// This enables the following two alternative HTTP JSON to RPC mappings:\n//\n// HTTP | gRPC\n// -----|-----\n// `GET /v1/messages/123456` | `GetMessage(message_id: \"123456\")`\n// `GET /v1/users/me/messages/123456` | `GetMessage(user_id: \"me\" message_id:\n// \"123456\")`\n//\n// ## Rules for HTTP mapping\n//\n// 1. Leaf request fields (recursive expansion nested messages in the request\n//    message) are classified into three categories:\n//    - Fields referred by the path template. They are passed via the URL path.\n//    - Fields referred by the [HttpRule.body][google.api.HttpRule.body]. They are passed via the HTTP\n//      request body.\n//    - All other fields are passed via the URL query parameters, and the\n//      parameter name is the field path in the request message. A repeated\n//      field can be represented as multiple query parameters under the same\n//      name.\n//  2. If [HttpRule.body][google.api.HttpRule.body] is \"*\", there is no URL query parameter, all fields\n//     are passed via URL path and HTTP request body.\n//  3. If [HttpRule.body][google.api.HttpRule.body] is omitted, there is no HTTP request body, all\n//     fields are passed via URL path and URL query parameters.\n//\n// ### Path template syntax\n//\n//     Template = \"/\" Segments [ Verb ] ;\n//     Segments = Segment { \"/\" Segment } ;\n//     Segment  = \"*\" | \"**\" | LITERAL | Variable ;\n//     Variable = \"{\" FieldPath [ \"=\" Segments ] \"}\" ;\n//     FieldPath = IDENT { \".\" IDENT } ;\n//     Verb     = \":\" LITERAL ;\n//\n// The syntax `*` matches a single URL path segment. The syntax `**` matches\n// zero or more URL path segments, which must be the last part of the URL path\n// except the `Verb`.\n//\n// The syntax `Variable` matches part of the URL path as specified by its\n// template. A variable template must not contain other variables. If a variable\n// matches a single path segment, its template may be omitted, e.g. `{var}`\n// is equivalent to `{var=*}`.\n//\n// The syntax `LITERAL` matches literal text in the URL path. If the `LITERAL`\n// contains any reserved character, such characters should be percent-encoded\n// before the matching.\n//\n// If a variable contains exactly one path segment, such as `\"{var}\"` or\n// `\"{var=*}\"`, when such a variable is expanded into a URL path on the client\n// side, all characters except `[-_.~0-9a-zA-Z]` are percent-encoded. The\n// server side does the reverse decoding. Such variables show up in the\n// [Discovery\n// Document](https://developers.google.com/discovery/v1/reference/apis) as\n// `{var}`.\n//\n// If a variable contains multiple path segments, such as `\"{var=foo/*}\"`\n// or `\"{var=**}\"`, when such a variable is expanded into a URL path on the\n// client side, all characters except `[-_.~/0-9a-zA-Z]` are percent-encoded.\n// The server side does the reverse decoding, except \"%2F\" and \"%2f\" are left\n// unchanged. Such variables show up in the\n// [Discovery\n// Document](https://developers.google.com/discovery/v1/reference/apis) as\n// `{+var}`.\n//\n// ## Using gRPC API Service Configuration\n//\n// gRPC API Service Configuration (service config) is a configuration language\n// for configuring a gRPC service to become a user-facing product. The\n// service config is simply the YAML representation of the `google.api.Service`\n// proto message.\n//\n// As an alternative to annotating your proto file, you can configure gRPC\n// transcoding in your service config YAML files. You do this by specifying a\n// `HttpRule` that maps the gRPC method to a REST endpoint, achieving the same\n// effect as the proto annotation. This can be particularly useful if you\n// have a proto that is reused in multiple services. Note that any transcoding\n// specified in the service config will override any matching transcoding\n// configuration in the proto.\n//\n// Example:\n//\n//     http:\n//       rules:\n//         # Selects a gRPC method and applies HttpRule to it.\n//         - selector: example.v1.Messaging.GetMessage\n//           get: /v1/messages/{message_id}/{sub.subfield}\n//\n// ## Special notes\n//\n// When gRPC Transcoding is used to map a gRPC to JSON REST endpoints, the\n// proto to JSON conversion must follow the [proto3\n// specification](https://developers.google.com/protocol-buffers/docs/proto3#json).\n//\n// While the single segment variable follows the semantics of\n// [RFC 6570](https://tools.ietf.org/html/rfc6570) Section 3.2.2 Simple String\n// Expansion, the multi segment variable **does not** follow RFC 6570 Section\n// 3.2.3 Reserved Expansion. The reason is that the Reserved Expansion\n// does not expand special characters like `?` and `#`, which would lead\n// to invalid URLs. As the result, gRPC Transcoding uses a custom encoding\n// for multi segment variables.\n//\n// The path variables **must not** refer to any repeated or mapped field,\n// because client libraries are not capable of handling such variable expansion.\n//\n// The path variables **must not** capture the leading \"/\" character. The reason\n// is that the most common use case \"{var}\" does not capture the leading \"/\"\n// character. For consistency, all path variables must share the same behavior.\n//\n// Repeated message fields must not be mapped to URL query parameters, because\n// no client library can support such complicated mapping.\n//\n// If an API needs to use a JSON array for request or response body, it can map\n// the request or response body to a repeated field. However, some gRPC\n// Transcoding implementations may not support this feature.\nmessage HttpRule {\n  // Selects a method to which this rule applies.\n  //\n  // Refer to [selector][google.api.DocumentationRule.selector] for syntax details.\n  string selector = 1;\n\n  // Determines the URL pattern is matched by this rules. This pattern can be\n  // used with any of the {get|put|post|delete|patch} methods. A custom method\n  // can be defined using the 'custom' field.\n  oneof pattern {\n    // Maps to HTTP GET. Used for listing and getting information about\n    // resources.\n    string get = 2;\n\n    // Maps to HTTP PUT. Used for replacing a resource.\n    string put = 3;\n\n    // Maps to HTTP POST. Used for creating a resource or performing an action.\n    string post = 4;\n\n    // Maps to HTTP DELETE. Used for deleting a resource.\n    string delete = 5;\n\n    // Maps to HTTP PATCH. Used for updating a resource.\n    string patch = 6;\n\n    // The custom pattern is used for specifying an HTTP method that is not\n    // included in the `pattern` field, such as HEAD, or \"*\" to leave the\n    // HTTP method unspecified for this rule. The wild-card rule is useful\n    // for services that provide content to Web (HTML) clients.\n    CustomHttpPattern custom = 8;\n  }\n\n  // The name of the request field whose value is mapped to the HTTP request\n  // body, or `*` for mapping all request fields not captured by the path\n  // pattern to the HTTP body, or omitted for not having any HTTP request body.\n  //\n  // NOTE: the referred field must be present at the top-level of the request\n  // message type.\n  string body = 7;\n\n  // Optional. The name of the response field whose value is mapped to the HTTP\n  // response body. When omitted, the entire response message will be used\n  // as the HTTP response body.\n  //\n  // NOTE: The referred field must be present at the top-level of the response\n  // message type.\n  string response_body = 12;\n\n  // Additional HTTP bindings for the selector. Nested bindings must\n  // not contain an `additional_bindings` field themselves (that is,\n  // the nesting may only be one level deep).\n  repeated HttpRule additional_bindings = 11;\n}\n\n// A custom pattern is used for defining custom HTTP verb.\nmessage CustomHttpPattern {\n  // The name of this custom HTTP verb.\n  string kind = 1;\n\n  // The path matched by this custom verb.\n  string path = 2;\n}\n"
  },
  {
    "path": "third_party/google/api/httpbody.proto",
    "content": "// Copyright 2020 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nsyntax = \"proto3\";\n\npackage google.api;\n\nimport \"google/protobuf/any.proto\";\n\noption cc_enable_arenas = true;\noption go_package = \"google.golang.org/genproto/googleapis/api/httpbody;httpbody\";\noption java_multiple_files = true;\noption java_outer_classname = \"HttpBodyProto\";\noption java_package = \"com.google.api\";\noption objc_class_prefix = \"GAPI\";\n\n// Message that represents an arbitrary HTTP body. It should only be used for\n// payload formats that can't be represented as JSON, such as raw binary or\n// an HTML page.\n//\n//\n// This message can be used both in streaming and non-streaming API methods in\n// the request as well as the response.\n//\n// It can be used as a top-level request field, which is convenient if one\n// wants to extract parameters from either the URL or HTTP template into the\n// request fields and also want access to the raw HTTP body.\n//\n// Example:\n//\n//     message GetResourceRequest {\n//       // A unique request id.\n//       string request_id = 1;\n//\n//       // The raw HTTP body is bound to this field.\n//       google.api.HttpBody http_body = 2;\n//     }\n//\n//     service ResourceService {\n//       rpc GetResource(GetResourceRequest) returns (google.api.HttpBody);\n//       rpc UpdateResource(google.api.HttpBody) returns\n//       (google.protobuf.Empty);\n//     }\n//\n// Example with streaming methods:\n//\n//     service CaldavService {\n//       rpc GetCalendar(stream google.api.HttpBody)\n//         returns (stream google.api.HttpBody);\n//       rpc UpdateCalendar(stream google.api.HttpBody)\n//         returns (stream google.api.HttpBody);\n//     }\n//\n// Use of this type only changes how the request and response bodies are\n// handled, all other features will continue to work unchanged.\nmessage HttpBody {\n  // The HTTP Content-Type header value specifying the content type of the body.\n  string content_type = 1;\n\n  // The HTTP request/response body as raw binary.\n  bytes data = 2;\n\n  // Application specific response metadata. Must be set in the first response\n  // for streaming APIs.\n  repeated google.protobuf.Any extensions = 3;\n}\n"
  },
  {
    "path": "third_party/google/protobuf/descriptor.proto",
    "content": "// Protocol Buffers - Google's data interchange format\n// Copyright 2008 Google Inc.  All rights reserved.\n// https://developers.google.com/protocol-buffers/\n//\n// Redistribution and use in source and binary forms, with or without\n// modification, are permitted provided that the following conditions are\n// met:\n//\n//     * Redistributions of source code must retain the above copyright\n// notice, this list of conditions and the following disclaimer.\n//     * Redistributions in binary form must reproduce the above\n// copyright notice, this list of conditions and the following disclaimer\n// in the documentation and/or other materials provided with the\n// distribution.\n//     * Neither the name of Google Inc. nor the names of its\n// contributors may be used to endorse or promote products derived from\n// this software without specific prior written permission.\n//\n// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n// \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\n// Author: kenton@google.com (Kenton Varda)\n//  Based on original Protocol Buffers design by\n//  Sanjay Ghemawat, Jeff Dean, and others.\n//\n// The messages in this file describe the definitions found in .proto files.\n// A valid .proto file can be translated directly to a FileDescriptorProto\n// without any other information (e.g. without reading its imports).\n\n\nsyntax = \"proto2\";\n\npackage google.protobuf;\n\noption go_package = \"google.golang.org/protobuf/types/descriptorpb\";\noption java_package = \"com.google.protobuf\";\noption java_outer_classname = \"DescriptorProtos\";\noption csharp_namespace = \"Google.Protobuf.Reflection\";\noption objc_class_prefix = \"GPB\";\noption cc_enable_arenas = true;\n\n// descriptor.proto must be optimized for speed because reflection-based\n// algorithms don't work during bootstrapping.\noption optimize_for = SPEED;\n\n// The protocol compiler can output a FileDescriptorSet containing the .proto\n// files it parses.\nmessage FileDescriptorSet {\n  repeated FileDescriptorProto file = 1;\n}\n\n// Describes a complete .proto file.\nmessage FileDescriptorProto {\n  optional string name = 1;     // file name, relative to root of source tree\n  optional string package = 2;  // e.g. \"foo\", \"foo.bar\", etc.\n\n  // Names of files imported by this file.\n  repeated string dependency = 3;\n  // Indexes of the public imported files in the dependency list above.\n  repeated int32 public_dependency = 10;\n  // Indexes of the weak imported files in the dependency list.\n  // For Google-internal migration only. Do not use.\n  repeated int32 weak_dependency = 11;\n\n  // All top-level definitions in this file.\n  repeated DescriptorProto message_type = 4;\n  repeated EnumDescriptorProto enum_type = 5;\n  repeated ServiceDescriptorProto service = 6;\n  repeated FieldDescriptorProto extension = 7;\n\n  optional FileOptions options = 8;\n\n  // This field contains optional information about the original source code.\n  // You may safely remove this entire field without harming runtime\n  // functionality of the descriptors -- the information is needed only by\n  // development tools.\n  optional SourceCodeInfo source_code_info = 9;\n\n  // The syntax of the proto file.\n  // The supported values are \"proto2\" and \"proto3\".\n  optional string syntax = 12;\n}\n\n// Describes a message type.\nmessage DescriptorProto {\n  optional string name = 1;\n\n  repeated FieldDescriptorProto field = 2;\n  repeated FieldDescriptorProto extension = 6;\n\n  repeated DescriptorProto nested_type = 3;\n  repeated EnumDescriptorProto enum_type = 4;\n\n  message ExtensionRange {\n    optional int32 start = 1;  // Inclusive.\n    optional int32 end = 2;    // Exclusive.\n\n    optional ExtensionRangeOptions options = 3;\n  }\n  repeated ExtensionRange extension_range = 5;\n\n  repeated OneofDescriptorProto oneof_decl = 8;\n\n  optional MessageOptions options = 7;\n\n  // Range of reserved tag numbers. Reserved tag numbers may not be used by\n  // fields or extension ranges in the same message. Reserved ranges may\n  // not overlap.\n  message ReservedRange {\n    optional int32 start = 1;  // Inclusive.\n    optional int32 end = 2;    // Exclusive.\n  }\n  repeated ReservedRange reserved_range = 9;\n  // Reserved field names, which may not be used by fields in the same message.\n  // A given name may only be reserved once.\n  repeated string reserved_name = 10;\n}\n\nmessage ExtensionRangeOptions {\n  // The parser stores options it doesn't recognize here. See above.\n  repeated UninterpretedOption uninterpreted_option = 999;\n\n\n  // Clients can define custom options in extensions of this message. See above.\n  extensions 1000 to max;\n}\n\n// Describes a field within a message.\nmessage FieldDescriptorProto {\n  enum Type {\n    // 0 is reserved for errors.\n    // Order is weird for historical reasons.\n    TYPE_DOUBLE = 1;\n    TYPE_FLOAT = 2;\n    // Not ZigZag encoded.  Negative numbers take 10 bytes.  Use TYPE_SINT64 if\n    // negative values are likely.\n    TYPE_INT64 = 3;\n    TYPE_UINT64 = 4;\n    // Not ZigZag encoded.  Negative numbers take 10 bytes.  Use TYPE_SINT32 if\n    // negative values are likely.\n    TYPE_INT32 = 5;\n    TYPE_FIXED64 = 6;\n    TYPE_FIXED32 = 7;\n    TYPE_BOOL = 8;\n    TYPE_STRING = 9;\n    // Tag-delimited aggregate.\n    // Group type is deprecated and not supported in proto3. However, Proto3\n    // implementations should still be able to parse the group wire format and\n    // treat group fields as unknown fields.\n    TYPE_GROUP = 10;\n    TYPE_MESSAGE = 11;  // Length-delimited aggregate.\n\n    // New in version 2.\n    TYPE_BYTES = 12;\n    TYPE_UINT32 = 13;\n    TYPE_ENUM = 14;\n    TYPE_SFIXED32 = 15;\n    TYPE_SFIXED64 = 16;\n    TYPE_SINT32 = 17;  // Uses ZigZag encoding.\n    TYPE_SINT64 = 18;  // Uses ZigZag encoding.\n  }\n\n  enum Label {\n    // 0 is reserved for errors\n    LABEL_OPTIONAL = 1;\n    LABEL_REQUIRED = 2;\n    LABEL_REPEATED = 3;\n  }\n\n  optional string name = 1;\n  optional int32 number = 3;\n  optional Label label = 4;\n\n  // If type_name is set, this need not be set.  If both this and type_name\n  // are set, this must be one of TYPE_ENUM, TYPE_MESSAGE or TYPE_GROUP.\n  optional Type type = 5;\n\n  // For message and enum types, this is the name of the type.  If the name\n  // starts with a '.', it is fully-qualified.  Otherwise, C++-like scoping\n  // rules are used to find the type (i.e. first the nested types within this\n  // message are searched, then within the parent, on up to the root\n  // namespace).\n  optional string type_name = 6;\n\n  // For extensions, this is the name of the type being extended.  It is\n  // resolved in the same manner as type_name.\n  optional string extendee = 2;\n\n  // For numeric types, contains the original text representation of the value.\n  // For booleans, \"true\" or \"false\".\n  // For strings, contains the default text contents (not escaped in any way).\n  // For bytes, contains the C escaped value.  All bytes >= 128 are escaped.\n  // TODO(kenton):  Base-64 encode?\n  optional string default_value = 7;\n\n  // If set, gives the index of a oneof in the containing type's oneof_decl\n  // list.  This field is a member of that oneof.\n  optional int32 oneof_index = 9;\n\n  // JSON name of this field. The value is set by protocol compiler. If the\n  // user has set a \"json_name\" option on this field, that option's value\n  // will be used. Otherwise, it's deduced from the field's name by converting\n  // it to camelCase.\n  optional string json_name = 10;\n\n  optional FieldOptions options = 8;\n\n  // If true, this is a proto3 \"optional\". When a proto3 field is optional, it\n  // tracks presence regardless of field type.\n  //\n  // When proto3_optional is true, this field must be belong to a oneof to\n  // signal to old proto3 clients that presence is tracked for this field. This\n  // oneof is known as a \"synthetic\" oneof, and this field must be its sole\n  // member (each proto3 optional field gets its own synthetic oneof). Synthetic\n  // oneofs exist in the descriptor only, and do not generate any API. Synthetic\n  // oneofs must be ordered after all \"real\" oneofs.\n  //\n  // For message fields, proto3_optional doesn't create any semantic change,\n  // since non-repeated message fields always track presence. However it still\n  // indicates the semantic detail of whether the user wrote \"optional\" or not.\n  // This can be useful for round-tripping the .proto file. For consistency we\n  // give message fields a synthetic oneof also, even though it is not required\n  // to track presence. This is especially important because the parser can't\n  // tell if a field is a message or an enum, so it must always create a\n  // synthetic oneof.\n  //\n  // Proto2 optional fields do not set this flag, because they already indicate\n  // optional with `LABEL_OPTIONAL`.\n  optional bool proto3_optional = 17;\n}\n\n// Describes a oneof.\nmessage OneofDescriptorProto {\n  optional string name = 1;\n  optional OneofOptions options = 2;\n}\n\n// Describes an enum type.\nmessage EnumDescriptorProto {\n  optional string name = 1;\n\n  repeated EnumValueDescriptorProto value = 2;\n\n  optional EnumOptions options = 3;\n\n  // Range of reserved numeric values. Reserved values may not be used by\n  // entries in the same enum. Reserved ranges may not overlap.\n  //\n  // Note that this is distinct from DescriptorProto.ReservedRange in that it\n  // is inclusive such that it can appropriately represent the entire int32\n  // domain.\n  message EnumReservedRange {\n    optional int32 start = 1;  // Inclusive.\n    optional int32 end = 2;    // Inclusive.\n  }\n\n  // Range of reserved numeric values. Reserved numeric values may not be used\n  // by enum values in the same enum declaration. Reserved ranges may not\n  // overlap.\n  repeated EnumReservedRange reserved_range = 4;\n\n  // Reserved enum value names, which may not be reused. A given name may only\n  // be reserved once.\n  repeated string reserved_name = 5;\n}\n\n// Describes a value within an enum.\nmessage EnumValueDescriptorProto {\n  optional string name = 1;\n  optional int32 number = 2;\n\n  optional EnumValueOptions options = 3;\n}\n\n// Describes a service.\nmessage ServiceDescriptorProto {\n  optional string name = 1;\n  repeated MethodDescriptorProto method = 2;\n\n  optional ServiceOptions options = 3;\n}\n\n// Describes a method of a service.\nmessage MethodDescriptorProto {\n  optional string name = 1;\n\n  // Input and output type names.  These are resolved in the same way as\n  // FieldDescriptorProto.type_name, but must refer to a message type.\n  optional string input_type = 2;\n  optional string output_type = 3;\n\n  optional MethodOptions options = 4;\n\n  // Identifies if client streams multiple client messages\n  optional bool client_streaming = 5 [default = false];\n  // Identifies if server streams multiple server messages\n  optional bool server_streaming = 6 [default = false];\n}\n\n\n// ===================================================================\n// Options\n\n// Each of the definitions above may have \"options\" attached.  These are\n// just annotations which may cause code to be generated slightly differently\n// or may contain hints for code that manipulates protocol messages.\n//\n// Clients may define custom options as extensions of the *Options messages.\n// These extensions may not yet be known at parsing time, so the parser cannot\n// store the values in them.  Instead it stores them in a field in the *Options\n// message called uninterpreted_option. This field must have the same name\n// across all *Options messages. We then use this field to populate the\n// extensions when we build a descriptor, at which point all protos have been\n// parsed and so all extensions are known.\n//\n// Extension numbers for custom options may be chosen as follows:\n// * For options which will only be used within a single application or\n//   organization, or for experimental options, use field numbers 50000\n//   through 99999.  It is up to you to ensure that you do not use the\n//   same number for multiple options.\n// * For options which will be published and used publicly by multiple\n//   independent entities, e-mail protobuf-global-extension-registry@google.com\n//   to reserve extension numbers. Simply provide your project name (e.g.\n//   Objective-C plugin) and your project website (if available) -- there's no\n//   need to explain how you intend to use them. Usually you only need one\n//   extension number. You can declare multiple options with only one extension\n//   number by putting them in a sub-message. See the Custom Options section of\n//   the docs for examples:\n//   https://developers.google.com/protocol-buffers/docs/proto#options\n//   If this turns out to be popular, a web service will be set up\n//   to automatically assign option numbers.\n\nmessage FileOptions {\n\n  // Sets the Java package where classes generated from this .proto will be\n  // placed.  By default, the proto package is used, but this is often\n  // inappropriate because proto packages do not normally start with backwards\n  // domain names.\n  optional string java_package = 1;\n\n\n  // Controls the name of the wrapper Java class generated for the .proto file.\n  // That class will always contain the .proto file's getDescriptor() method as\n  // well as any top-level extensions defined in the .proto file.\n  // If java_multiple_files is disabled, then all the other classes from the\n  // .proto file will be nested inside the single wrapper outer class.\n  optional string java_outer_classname = 8;\n\n  // If enabled, then the Java code generator will generate a separate .java\n  // file for each top-level message, enum, and service defined in the .proto\n  // file.  Thus, these types will *not* be nested inside the wrapper class\n  // named by java_outer_classname.  However, the wrapper class will still be\n  // generated to contain the file's getDescriptor() method as well as any\n  // top-level extensions defined in the file.\n  optional bool java_multiple_files = 10 [default = false];\n\n  // This option does nothing.\n  optional bool java_generate_equals_and_hash = 20 [deprecated=true];\n\n  // If set true, then the Java2 code generator will generate code that\n  // throws an exception whenever an attempt is made to assign a non-UTF-8\n  // byte sequence to a string field.\n  // Message reflection will do the same.\n  // However, an extension field still accepts non-UTF-8 byte sequences.\n  // This option has no effect on when used with the lite runtime.\n  optional bool java_string_check_utf8 = 27 [default = false];\n\n\n  // Generated classes can be optimized for speed or code size.\n  enum OptimizeMode {\n    SPEED = 1;         // Generate complete code for parsing, serialization,\n    // etc.\n    CODE_SIZE = 2;     // Use ReflectionOps to implement these methods.\n    LITE_RUNTIME = 3;  // Generate code using MessageLite and the lite runtime.\n  }\n  optional OptimizeMode optimize_for = 9 [default = SPEED];\n\n  // Sets the Go package where structs generated from this .proto will be\n  // placed. If omitted, the Go package will be derived from the following:\n  //   - The basename of the package import path, if provided.\n  //   - Otherwise, the package statement in the .proto file, if present.\n  //   - Otherwise, the basename of the .proto file, without extension.\n  optional string go_package = 11;\n\n\n\n\n  // Should generic services be generated in each language?  \"Generic\" services\n  // are not specific to any particular RPC system.  They are generated by the\n  // main code generators in each language (without additional plugins).\n  // Generic services were the only kind of service generation supported by\n  // early versions of google.protobuf.\n  //\n  // Generic services are now considered deprecated in favor of using plugins\n  // that generate code specific to your particular RPC system.  Therefore,\n  // these default to false.  Old code which depends on generic services should\n  // explicitly set them to true.\n  optional bool cc_generic_services = 16 [default = false];\n  optional bool java_generic_services = 17 [default = false];\n  optional bool py_generic_services = 18 [default = false];\n  optional bool php_generic_services = 42 [default = false];\n\n  // Is this file deprecated?\n  // Depending on the target platform, this can emit Deprecated annotations\n  // for everything in the file, or it will be completely ignored; in the very\n  // least, this is a formalization for deprecating files.\n  optional bool deprecated = 23 [default = false];\n\n  // Enables the use of arenas for the proto messages in this file. This applies\n  // only to generated classes for C++.\n  optional bool cc_enable_arenas = 31 [default = true];\n\n\n  // Sets the objective c class prefix which is prepended to all objective c\n  // generated classes from this .proto. There is no default.\n  optional string objc_class_prefix = 36;\n\n  // Namespace for generated classes; defaults to the package.\n  optional string csharp_namespace = 37;\n\n  // By default Swift generators will take the proto package and CamelCase it\n  // replacing '.' with underscore and use that to prefix the types/symbols\n  // defined. When this options is provided, they will use this value instead\n  // to prefix the types/symbols defined.\n  optional string swift_prefix = 39;\n\n  // Sets the php class prefix which is prepended to all php generated classes\n  // from this .proto. Default is empty.\n  optional string php_class_prefix = 40;\n\n  // Use this option to change the namespace of php generated classes. Default\n  // is empty. When this option is empty, the package name will be used for\n  // determining the namespace.\n  optional string php_namespace = 41;\n\n  // Use this option to change the namespace of php generated metadata classes.\n  // Default is empty. When this option is empty, the proto file name will be\n  // used for determining the namespace.\n  optional string php_metadata_namespace = 44;\n\n  // Use this option to change the package of ruby generated classes. Default\n  // is empty. When this option is not set, the package name will be used for\n  // determining the ruby package.\n  optional string ruby_package = 45;\n\n\n  // The parser stores options it doesn't recognize here.\n  // See the documentation for the \"Options\" section above.\n  repeated UninterpretedOption uninterpreted_option = 999;\n\n  // Clients can define custom options in extensions of this message.\n  // See the documentation for the \"Options\" section above.\n  extensions 1000 to max;\n\n  reserved 38;\n}\n\nmessage MessageOptions {\n  // Set true to use the old proto1 MessageSet wire format for extensions.\n  // This is provided for backwards-compatibility with the MessageSet wire\n  // format.  You should not use this for any other reason:  It's less\n  // efficient, has fewer features, and is more complicated.\n  //\n  // The message must be defined exactly as follows:\n  //   message Foo {\n  //     option message_set_wire_format = true;\n  //     extensions 4 to max;\n  //   }\n  // Note that the message cannot have any defined fields; MessageSets only\n  // have extensions.\n  //\n  // All extensions of your type must be singular messages; e.g. they cannot\n  // be int32s, enums, or repeated messages.\n  //\n  // Because this is an option, the above two restrictions are not enforced by\n  // the protocol compiler.\n  optional bool message_set_wire_format = 1 [default = false];\n\n  // Disables the generation of the standard \"descriptor()\" accessor, which can\n  // conflict with a field of the same name.  This is meant to make migration\n  // from proto1 easier; new code should avoid fields named \"descriptor\".\n  optional bool no_standard_descriptor_accessor = 2 [default = false];\n\n  // Is this message deprecated?\n  // Depending on the target platform, this can emit Deprecated annotations\n  // for the message, or it will be completely ignored; in the very least,\n  // this is a formalization for deprecating messages.\n  optional bool deprecated = 3 [default = false];\n\n  reserved 4, 5, 6;\n\n  // Whether the message is an automatically generated map entry type for the\n  // maps field.\n  //\n  // For maps fields:\n  //     map<KeyType, ValueType> map_field = 1;\n  // The parsed descriptor looks like:\n  //     message MapFieldEntry {\n  //         option map_entry = true;\n  //         optional KeyType key = 1;\n  //         optional ValueType value = 2;\n  //     }\n  //     repeated MapFieldEntry map_field = 1;\n  //\n  // Implementations may choose not to generate the map_entry=true message, but\n  // use a native map in the target language to hold the keys and values.\n  // The reflection APIs in such implementations still need to work as\n  // if the field is a repeated message field.\n  //\n  // NOTE: Do not set the option in .proto files. Always use the maps syntax\n  // instead. The option should only be implicitly set by the proto compiler\n  // parser.\n  optional bool map_entry = 7;\n\n  reserved 8;  // javalite_serializable\n  reserved 9;  // javanano_as_lite\n\n\n  // The parser stores options it doesn't recognize here. See above.\n  repeated UninterpretedOption uninterpreted_option = 999;\n\n  // Clients can define custom options in extensions of this message. See above.\n  extensions 1000 to max;\n}\n\nmessage FieldOptions {\n  // The ctype option instructs the C++ code generator to use a different\n  // representation of the field than it normally would.  See the specific\n  // options below.  This option is not yet implemented in the open source\n  // release -- sorry, we'll try to include it in a future version!\n  optional CType ctype = 1 [default = STRING];\n  enum CType {\n    // Default mode.\n    STRING = 0;\n\n    CORD = 1;\n\n    STRING_PIECE = 2;\n  }\n  // The packed option can be enabled for repeated primitive fields to enable\n  // a more efficient representation on the wire. Rather than repeatedly\n  // writing the tag and type for each element, the entire array is encoded as\n  // a single length-delimited blob. In proto3, only explicit setting it to\n  // false will avoid using packed encoding.\n  optional bool packed = 2;\n\n  // The jstype option determines the JavaScript type used for values of the\n  // field.  The option is permitted only for 64 bit integral and fixed types\n  // (int64, uint64, sint64, fixed64, sfixed64).  A field with jstype JS_STRING\n  // is represented as JavaScript string, which avoids loss of precision that\n  // can happen when a large value is converted to a floating point JavaScript.\n  // Specifying JS_NUMBER for the jstype causes the generated JavaScript code to\n  // use the JavaScript \"number\" type.  The behavior of the default option\n  // JS_NORMAL is implementation dependent.\n  //\n  // This option is an enum to permit additional types to be added, e.g.\n  // goog.math.Integer.\n  optional JSType jstype = 6 [default = JS_NORMAL];\n  enum JSType {\n    // Use the default type.\n    JS_NORMAL = 0;\n\n    // Use JavaScript strings.\n    JS_STRING = 1;\n\n    // Use JavaScript numbers.\n    JS_NUMBER = 2;\n  }\n\n  // Should this field be parsed lazily?  Lazy applies only to message-type\n  // fields.  It means that when the outer message is initially parsed, the\n  // inner message's contents will not be parsed but instead stored in encoded\n  // form.  The inner message will actually be parsed when it is first accessed.\n  //\n  // This is only a hint.  Implementations are free to choose whether to use\n  // eager or lazy parsing regardless of the value of this option.  However,\n  // setting this option true suggests that the protocol author believes that\n  // using lazy parsing on this field is worth the additional bookkeeping\n  // overhead typically needed to implement it.\n  //\n  // This option does not affect the public interface of any generated code;\n  // all method signatures remain the same.  Furthermore, thread-safety of the\n  // interface is not affected by this option; const methods remain safe to\n  // call from multiple threads concurrently, while non-const methods continue\n  // to require exclusive access.\n  //\n  //\n  // Note that implementations may choose not to check required fields within\n  // a lazy sub-message.  That is, calling IsInitialized() on the outer message\n  // may return true even if the inner message has missing required fields.\n  // This is necessary because otherwise the inner message would have to be\n  // parsed in order to perform the check, defeating the purpose of lazy\n  // parsing.  An implementation which chooses not to check required fields\n  // must be consistent about it.  That is, for any particular sub-message, the\n  // implementation must either *always* check its required fields, or *never*\n  // check its required fields, regardless of whether or not the message has\n  // been parsed.\n  optional bool lazy = 5 [default = false];\n\n  // Is this field deprecated?\n  // Depending on the target platform, this can emit Deprecated annotations\n  // for accessors, or it will be completely ignored; in the very least, this\n  // is a formalization for deprecating fields.\n  optional bool deprecated = 3 [default = false];\n\n  // For Google-internal migration only. Do not use.\n  optional bool weak = 10 [default = false];\n\n\n  // The parser stores options it doesn't recognize here. See above.\n  repeated UninterpretedOption uninterpreted_option = 999;\n\n  // Clients can define custom options in extensions of this message. See above.\n  extensions 1000 to max;\n\n  reserved 4;  // removed jtype\n}\n\nmessage OneofOptions {\n  // The parser stores options it doesn't recognize here. See above.\n  repeated UninterpretedOption uninterpreted_option = 999;\n\n  // Clients can define custom options in extensions of this message. See above.\n  extensions 1000 to max;\n}\n\nmessage EnumOptions {\n\n  // Set this option to true to allow mapping different tag names to the same\n  // value.\n  optional bool allow_alias = 2;\n\n  // Is this enum deprecated?\n  // Depending on the target platform, this can emit Deprecated annotations\n  // for the enum, or it will be completely ignored; in the very least, this\n  // is a formalization for deprecating enums.\n  optional bool deprecated = 3 [default = false];\n\n  reserved 5;  // javanano_as_lite\n\n  // The parser stores options it doesn't recognize here. See above.\n  repeated UninterpretedOption uninterpreted_option = 999;\n\n  // Clients can define custom options in extensions of this message. See above.\n  extensions 1000 to max;\n}\n\nmessage EnumValueOptions {\n  // Is this enum value deprecated?\n  // Depending on the target platform, this can emit Deprecated annotations\n  // for the enum value, or it will be completely ignored; in the very least,\n  // this is a formalization for deprecating enum values.\n  optional bool deprecated = 1 [default = false];\n\n  // The parser stores options it doesn't recognize here. See above.\n  repeated UninterpretedOption uninterpreted_option = 999;\n\n  // Clients can define custom options in extensions of this message. See above.\n  extensions 1000 to max;\n}\n\nmessage ServiceOptions {\n\n  // Note:  Field numbers 1 through 32 are reserved for Google's internal RPC\n  //   framework.  We apologize for hoarding these numbers to ourselves, but\n  //   we were already using them long before we decided to release Protocol\n  //   Buffers.\n\n  // Is this service deprecated?\n  // Depending on the target platform, this can emit Deprecated annotations\n  // for the service, or it will be completely ignored; in the very least,\n  // this is a formalization for deprecating services.\n  optional bool deprecated = 33 [default = false];\n\n  // The parser stores options it doesn't recognize here. See above.\n  repeated UninterpretedOption uninterpreted_option = 999;\n\n  // Clients can define custom options in extensions of this message. See above.\n  extensions 1000 to max;\n}\n\nmessage MethodOptions {\n\n  // Note:  Field numbers 1 through 32 are reserved for Google's internal RPC\n  //   framework.  We apologize for hoarding these numbers to ourselves, but\n  //   we were already using them long before we decided to release Protocol\n  //   Buffers.\n\n  // Is this method deprecated?\n  // Depending on the target platform, this can emit Deprecated annotations\n  // for the method, or it will be completely ignored; in the very least,\n  // this is a formalization for deprecating methods.\n  optional bool deprecated = 33 [default = false];\n\n  // Is this method side-effect-free (or safe in HTTP parlance), or idempotent,\n  // or neither? HTTP based RPC implementation may choose GET verb for safe\n  // methods, and PUT verb for idempotent methods instead of the default POST.\n  enum IdempotencyLevel {\n    IDEMPOTENCY_UNKNOWN = 0;\n    NO_SIDE_EFFECTS = 1;  // implies idempotent\n    IDEMPOTENT = 2;       // idempotent, but may have side effects\n  }\n  optional IdempotencyLevel idempotency_level = 34\n  [default = IDEMPOTENCY_UNKNOWN];\n\n  // The parser stores options it doesn't recognize here. See above.\n  repeated UninterpretedOption uninterpreted_option = 999;\n\n  // Clients can define custom options in extensions of this message. See above.\n  extensions 1000 to max;\n}\n\n\n// A message representing a option the parser does not recognize. This only\n// appears in options protos created by the compiler::Parser class.\n// DescriptorPool resolves these when building Descriptor objects. Therefore,\n// options protos in descriptor objects (e.g. returned by Descriptor::options(),\n// or produced by Descriptor::CopyTo()) will never have UninterpretedOptions\n// in them.\nmessage UninterpretedOption {\n  // The name of the uninterpreted option.  Each string represents a segment in\n  // a dot-separated name.  is_extension is true iff a segment represents an\n  // extension (denoted with parentheses in options specs in .proto files).\n  // E.g.,{ [\"foo\", false], [\"bar.baz\", true], [\"qux\", false] } represents\n  // \"foo.(bar.baz).qux\".\n  message NamePart {\n    required string name_part = 1;\n    required bool is_extension = 2;\n  }\n  repeated NamePart name = 2;\n\n  // The value of the uninterpreted option, in whatever type the tokenizer\n  // identified it as during parsing. Exactly one of these should be set.\n  optional string identifier_value = 3;\n  optional uint64 positive_int_value = 4;\n  optional int64 negative_int_value = 5;\n  optional double double_value = 6;\n  optional bytes string_value = 7;\n  optional string aggregate_value = 8;\n}\n\n// ===================================================================\n// Optional source code info\n\n// Encapsulates information about the original source file from which a\n// FileDescriptorProto was generated.\nmessage SourceCodeInfo {\n  // A Location identifies a piece of source code in a .proto file which\n  // corresponds to a particular definition.  This information is intended\n  // to be useful to IDEs, code indexers, documentation generators, and similar\n  // tools.\n  //\n  // For example, say we have a file like:\n  //   message Foo {\n  //     optional string foo = 1;\n  //   }\n  // Let's look at just the field definition:\n  //   optional string foo = 1;\n  //   ^       ^^     ^^  ^  ^^^\n  //   a       bc     de  f  ghi\n  // We have the following locations:\n  //   span   path               represents\n  //   [a,i)  [ 4, 0, 2, 0 ]     The whole field definition.\n  //   [a,b)  [ 4, 0, 2, 0, 4 ]  The label (optional).\n  //   [c,d)  [ 4, 0, 2, 0, 5 ]  The type (string).\n  //   [e,f)  [ 4, 0, 2, 0, 1 ]  The name (foo).\n  //   [g,h)  [ 4, 0, 2, 0, 3 ]  The number (1).\n  //\n  // Notes:\n  // - A location may refer to a repeated field itself (i.e. not to any\n  //   particular index within it).  This is used whenever a set of elements are\n  //   logically enclosed in a single code segment.  For example, an entire\n  //   extend block (possibly containing multiple extension definitions) will\n  //   have an outer location whose path refers to the \"extensions\" repeated\n  //   field without an index.\n  // - Multiple locations may have the same path.  This happens when a single\n  //   logical declaration is spread out across multiple places.  The most\n  //   obvious example is the \"extend\" block again -- there may be multiple\n  //   extend blocks in the same scope, each of which will have the same path.\n  // - A location's span is not always a subset of its parent's span.  For\n  //   example, the \"extendee\" of an extension declaration appears at the\n  //   beginning of the \"extend\" block and is shared by all extensions within\n  //   the block.\n  // - Just because a location's span is a subset of some other location's span\n  //   does not mean that it is a descendant.  For example, a \"group\" defines\n  //   both a type and a field in a single declaration.  Thus, the locations\n  //   corresponding to the type and field and their components will overlap.\n  // - Code which tries to interpret locations should probably be designed to\n  //   ignore those that it doesn't understand, as more types of locations could\n  //   be recorded in the future.\n  repeated Location location = 1;\n  message Location {\n    // Identifies which part of the FileDescriptorProto was defined at this\n    // location.\n    //\n    // Each element is a field number or an index.  They form a path from\n    // the root FileDescriptorProto to the place where the definition occurs.  For\n    // example, this path:\n    //   [ 4, 3, 2, 7, 1 ]\n    // refers to:\n    //   file.message_type(3)  // 4, 3\n    //       .field(7)         // 2, 7\n    //       .name()           // 1\n    // This is because FileDescriptorProto.message_type has field number 4:\n    //   repeated DescriptorProto message_type = 4;\n    // and DescriptorProto.field has field number 2:\n    //   repeated FieldDescriptorProto field = 2;\n    // and FieldDescriptorProto.name has field number 1:\n    //   optional string name = 1;\n    //\n    // Thus, the above path gives the location of a field name.  If we removed\n    // the last element:\n    //   [ 4, 3, 2, 7 ]\n    // this path refers to the whole field declaration (from the beginning\n    // of the label to the terminating semicolon).\n    repeated int32 path = 1 [packed = true];\n\n    // Always has exactly three or four elements: start line, start column,\n    // end line (optional, otherwise assumed same as start line), end column.\n    // These are packed into a single field for efficiency.  Note that line\n    // and column numbers are zero-based -- typically you will want to add\n    // 1 to each before displaying to a user.\n    repeated int32 span = 2 [packed = true];\n\n    // If this SourceCodeInfo represents a complete declaration, these are any\n    // comments appearing before and after the declaration which appear to be\n    // attached to the declaration.\n    //\n    // A series of line comments appearing on consecutive lines, with no other\n    // tokens appearing on those lines, will be treated as a single comment.\n    //\n    // leading_detached_comments will keep paragraphs of comments that appear\n    // before (but not connected to) the current element. Each paragraph,\n    // separated by empty lines, will be one comment element in the repeated\n    // field.\n    //\n    // Only the comment content is provided; comment markers (e.g. //) are\n    // stripped out.  For block comments, leading whitespace and an asterisk\n    // will be stripped from the beginning of each line other than the first.\n    // Newlines are included in the output.\n    //\n    // Examples:\n    //\n    //   optional int32 foo = 1;  // Comment attached to foo.\n    //   // Comment attached to bar.\n    //   optional int32 bar = 2;\n    //\n    //   optional string baz = 3;\n    //   // Comment attached to baz.\n    //   // Another line attached to baz.\n    //\n    //   // Comment attached to qux.\n    //   //\n    //   // Another line attached to qux.\n    //   optional double qux = 4;\n    //\n    //   // Detached comment for corge. This is not leading or trailing comments\n    //   // to qux or corge because there are blank lines separating it from\n    //   // both.\n    //\n    //   // Detached comment for corge paragraph 2.\n    //\n    //   optional string corge = 5;\n    //   /* Block comment attached\n    //    * to corge.  Leading asterisks\n    //    * will be removed. */\n    //   /* Block comment attached to\n    //    * grault. */\n    //   optional int32 grault = 6;\n    //\n    //   // ignored detached comments.\n    optional string leading_comments = 3;\n    optional string trailing_comments = 4;\n    repeated string leading_detached_comments = 6;\n  }\n}\n\n// Describes the relationship between generated code and its original source\n// file. A GeneratedCodeInfo message is associated with only one generated\n// source file, but may contain references to different source .proto files.\nmessage GeneratedCodeInfo {\n  // An Annotation connects some span of text in generated code to an element\n  // of its generating .proto file.\n  repeated Annotation annotation = 1;\n  message Annotation {\n    // Identifies the element in the original source .proto file. This field\n    // is formatted the same as SourceCodeInfo.Location.path.\n    repeated int32 path = 1 [packed = true];\n\n    // Identifies the filesystem path to the original source .proto.\n    optional string source_file = 2;\n\n    // Identifies the starting offset in bytes in the generated code\n    // that relates to the identified object.\n    optional int32 begin = 3;\n\n    // Identifies the ending offset in bytes in the generated code that\n    // relates to the identified offset. The end offset should be one past\n    // the last relevant byte (so the length of the text = end - begin).\n    optional int32 end = 4;\n  }\n}"
  },
  {
    "path": "third_party/validate/README.md",
    "content": "# protoc-gen-validate (PGV)\n\n* https://github.com/envoyproxy/protoc-gen-validate\n"
  },
  {
    "path": "third_party/validate/validate.proto",
    "content": "syntax = \"proto2\";\npackage validate;\n\noption go_package = \"github.com/envoyproxy/protoc-gen-validate/validate\";\noption java_package = \"io.envoyproxy.pgv.validate\";\n\nimport \"google/protobuf/descriptor.proto\";\nimport \"google/protobuf/duration.proto\";\nimport \"google/protobuf/timestamp.proto\";\n\n// Validation rules applied at the message level\nextend google.protobuf.MessageOptions {\n    // Disabled nullifies any validation rules for this message, including any\n    // message fields associated with it that do support validation.\n    optional bool disabled = 1071;\n    // Ignore skips generation of validation methods for this message.\n    optional bool ignored = 1072;\n}\n\n// Validation rules applied at the oneof level\nextend google.protobuf.OneofOptions {\n    // Required ensures that exactly one the field options in a oneof is set;\n    // validation fails if no fields in the oneof are set.\n    optional bool required = 1071;\n}\n\n// Validation rules applied at the field level\nextend google.protobuf.FieldOptions {\n    // Rules specify the validations to be performed on this field. By default,\n    // no validation is performed against a field.\n    optional FieldRules rules = 1071;\n}\n\n// FieldRules encapsulates the rules for each type of field. Depending on the\n// field, the correct set should be used to ensure proper validations.\nmessage FieldRules {\n    optional MessageRules message = 17;\n    oneof type {\n        // Scalar Field Types\n        FloatRules    float    = 1;\n        DoubleRules   double   = 2;\n        Int32Rules    int32    = 3;\n        Int64Rules    int64    = 4;\n        UInt32Rules   uint32   = 5;\n        UInt64Rules   uint64   = 6;\n        SInt32Rules   sint32   = 7;\n        SInt64Rules   sint64   = 8;\n        Fixed32Rules  fixed32  = 9;\n        Fixed64Rules  fixed64  = 10;\n        SFixed32Rules sfixed32 = 11;\n        SFixed64Rules sfixed64 = 12;\n        BoolRules     bool     = 13;\n        StringRules   string   = 14;\n        BytesRules    bytes    = 15;\n\n        // Complex Field Types\n        EnumRules     enum     = 16;\n        RepeatedRules repeated = 18;\n        MapRules      map      = 19;\n\n        // Well-Known Field Types\n        AnyRules       any       = 20;\n        DurationRules  duration  = 21;\n        TimestampRules timestamp = 22;\n    }\n}\n\n// FloatRules describes the constraints applied to `float` values\nmessage FloatRules {\n    // Const specifies that this field must be exactly the specified value\n    optional float const = 1;\n\n    // Lt specifies that this field must be less than the specified value,\n    // exclusive\n    optional float lt = 2;\n\n    // Lte specifies that this field must be less than or equal to the\n    // specified value, inclusive\n    optional float lte = 3;\n\n    // Gt specifies that this field must be greater than the specified value,\n    // exclusive. If the value of Gt is larger than a specified Lt or Lte, the\n    // range is reversed.\n    optional float gt = 4;\n\n    // Gte specifies that this field must be greater than or equal to the\n    // specified value, inclusive. If the value of Gte is larger than a\n    // specified Lt or Lte, the range is reversed.\n    optional float gte = 5;\n\n    // In specifies that this field must be equal to one of the specified\n    // values\n    repeated float in = 6;\n\n    // NotIn specifies that this field cannot be equal to one of the specified\n    // values\n    repeated float not_in = 7;\n\n    // IgnoreEmpty specifies that the validation rules of this field should be\n    // evaluated only if the field is not empty\n    optional bool ignore_empty = 8;\n}\n\n// DoubleRules describes the constraints applied to `double` values\nmessage DoubleRules {\n    // Const specifies that this field must be exactly the specified value\n    optional double const = 1;\n\n    // Lt specifies that this field must be less than the specified value,\n    // exclusive\n    optional double lt = 2;\n\n    // Lte specifies that this field must be less than or equal to the\n    // specified value, inclusive\n    optional double lte = 3;\n\n    // Gt specifies that this field must be greater than the specified value,\n    // exclusive. If the value of Gt is larger than a specified Lt or Lte, the\n    // range is reversed.\n    optional double gt = 4;\n\n    // Gte specifies that this field must be greater than or equal to the\n    // specified value, inclusive. If the value of Gte is larger than a\n    // specified Lt or Lte, the range is reversed.\n    optional double gte = 5;\n\n    // In specifies that this field must be equal to one of the specified\n    // values\n    repeated double in = 6;\n\n    // NotIn specifies that this field cannot be equal to one of the specified\n    // values\n    repeated double not_in = 7;\n\n    // IgnoreEmpty specifies that the validation rules of this field should be\n    // evaluated only if the field is not empty\n    optional bool ignore_empty = 8;\n}\n\n// Int32Rules describes the constraints applied to `int32` values\nmessage Int32Rules {\n    // Const specifies that this field must be exactly the specified value\n    optional int32 const = 1;\n\n    // Lt specifies that this field must be less than the specified value,\n    // exclusive\n    optional int32 lt = 2;\n\n    // Lte specifies that this field must be less than or equal to the\n    // specified value, inclusive\n    optional int32 lte = 3;\n\n    // Gt specifies that this field must be greater than the specified value,\n    // exclusive. If the value of Gt is larger than a specified Lt or Lte, the\n    // range is reversed.\n    optional int32 gt = 4;\n\n    // Gte specifies that this field must be greater than or equal to the\n    // specified value, inclusive. If the value of Gte is larger than a\n    // specified Lt or Lte, the range is reversed.\n    optional int32 gte = 5;\n\n    // In specifies that this field must be equal to one of the specified\n    // values\n    repeated int32 in = 6;\n\n    // NotIn specifies that this field cannot be equal to one of the specified\n    // values\n    repeated int32 not_in = 7;\n\n    // IgnoreEmpty specifies that the validation rules of this field should be\n    // evaluated only if the field is not empty\n    optional bool ignore_empty = 8;\n}\n\n// Int64Rules describes the constraints applied to `int64` values\nmessage Int64Rules {\n    // Const specifies that this field must be exactly the specified value\n    optional int64 const = 1;\n\n    // Lt specifies that this field must be less than the specified value,\n    // exclusive\n    optional int64 lt = 2;\n\n    // Lte specifies that this field must be less than or equal to the\n    // specified value, inclusive\n    optional int64 lte = 3;\n\n    // Gt specifies that this field must be greater than the specified value,\n    // exclusive. If the value of Gt is larger than a specified Lt or Lte, the\n    // range is reversed.\n    optional int64 gt = 4;\n\n    // Gte specifies that this field must be greater than or equal to the\n    // specified value, inclusive. If the value of Gte is larger than a\n    // specified Lt or Lte, the range is reversed.\n    optional int64 gte = 5;\n\n    // In specifies that this field must be equal to one of the specified\n    // values\n    repeated int64 in = 6;\n\n    // NotIn specifies that this field cannot be equal to one of the specified\n    // values\n    repeated int64 not_in = 7;\n\n    // IgnoreEmpty specifies that the validation rules of this field should be\n    // evaluated only if the field is not empty\n    optional bool ignore_empty = 8;\n}\n\n// UInt32Rules describes the constraints applied to `uint32` values\nmessage UInt32Rules {\n    // Const specifies that this field must be exactly the specified value\n    optional uint32 const = 1;\n\n    // Lt specifies that this field must be less than the specified value,\n    // exclusive\n    optional uint32 lt = 2;\n\n    // Lte specifies that this field must be less than or equal to the\n    // specified value, inclusive\n    optional uint32 lte = 3;\n\n    // Gt specifies that this field must be greater than the specified value,\n    // exclusive. If the value of Gt is larger than a specified Lt or Lte, the\n    // range is reversed.\n    optional uint32 gt = 4;\n\n    // Gte specifies that this field must be greater than or equal to the\n    // specified value, inclusive. If the value of Gte is larger than a\n    // specified Lt or Lte, the range is reversed.\n    optional uint32 gte = 5;\n\n    // In specifies that this field must be equal to one of the specified\n    // values\n    repeated uint32 in = 6;\n\n    // NotIn specifies that this field cannot be equal to one of the specified\n    // values\n    repeated uint32 not_in = 7;\n\n    // IgnoreEmpty specifies that the validation rules of this field should be\n    // evaluated only if the field is not empty\n    optional bool ignore_empty = 8;\n}\n\n// UInt64Rules describes the constraints applied to `uint64` values\nmessage UInt64Rules {\n    // Const specifies that this field must be exactly the specified value\n    optional uint64 const = 1;\n\n    // Lt specifies that this field must be less than the specified value,\n    // exclusive\n    optional uint64 lt = 2;\n\n    // Lte specifies that this field must be less than or equal to the\n    // specified value, inclusive\n    optional uint64 lte = 3;\n\n    // Gt specifies that this field must be greater than the specified value,\n    // exclusive. If the value of Gt is larger than a specified Lt or Lte, the\n    // range is reversed.\n    optional uint64 gt = 4;\n\n    // Gte specifies that this field must be greater than or equal to the\n    // specified value, inclusive. If the value of Gte is larger than a\n    // specified Lt or Lte, the range is reversed.\n    optional uint64 gte = 5;\n\n    // In specifies that this field must be equal to one of the specified\n    // values\n    repeated uint64 in = 6;\n\n    // NotIn specifies that this field cannot be equal to one of the specified\n    // values\n    repeated uint64 not_in = 7;\n\n    // IgnoreEmpty specifies that the validation rules of this field should be\n    // evaluated only if the field is not empty\n    optional bool ignore_empty = 8;\n}\n\n// SInt32Rules describes the constraints applied to `sint32` values\nmessage SInt32Rules {\n    // Const specifies that this field must be exactly the specified value\n    optional sint32 const = 1;\n\n    // Lt specifies that this field must be less than the specified value,\n    // exclusive\n    optional sint32 lt = 2;\n\n    // Lte specifies that this field must be less than or equal to the\n    // specified value, inclusive\n    optional sint32 lte = 3;\n\n    // Gt specifies that this field must be greater than the specified value,\n    // exclusive. If the value of Gt is larger than a specified Lt or Lte, the\n    // range is reversed.\n    optional sint32 gt = 4;\n\n    // Gte specifies that this field must be greater than or equal to the\n    // specified value, inclusive. If the value of Gte is larger than a\n    // specified Lt or Lte, the range is reversed.\n    optional sint32 gte = 5;\n\n    // In specifies that this field must be equal to one of the specified\n    // values\n    repeated sint32 in = 6;\n\n    // NotIn specifies that this field cannot be equal to one of the specified\n    // values\n    repeated sint32 not_in = 7;\n\n    // IgnoreEmpty specifies that the validation rules of this field should be\n    // evaluated only if the field is not empty\n    optional bool ignore_empty = 8;\n}\n\n// SInt64Rules describes the constraints applied to `sint64` values\nmessage SInt64Rules {\n    // Const specifies that this field must be exactly the specified value\n    optional sint64 const = 1;\n\n    // Lt specifies that this field must be less than the specified value,\n    // exclusive\n    optional sint64 lt = 2;\n\n    // Lte specifies that this field must be less than or equal to the\n    // specified value, inclusive\n    optional sint64 lte = 3;\n\n    // Gt specifies that this field must be greater than the specified value,\n    // exclusive. If the value of Gt is larger than a specified Lt or Lte, the\n    // range is reversed.\n    optional sint64 gt = 4;\n\n    // Gte specifies that this field must be greater than or equal to the\n    // specified value, inclusive. If the value of Gte is larger than a\n    // specified Lt or Lte, the range is reversed.\n    optional sint64 gte = 5;\n\n    // In specifies that this field must be equal to one of the specified\n    // values\n    repeated sint64 in = 6;\n\n    // NotIn specifies that this field cannot be equal to one of the specified\n    // values\n    repeated sint64 not_in = 7;\n\n    // IgnoreEmpty specifies that the validation rules of this field should be\n    // evaluated only if the field is not empty\n    optional bool ignore_empty = 8;\n}\n\n// Fixed32Rules describes the constraints applied to `fixed32` values\nmessage Fixed32Rules {\n    // Const specifies that this field must be exactly the specified value\n    optional fixed32 const = 1;\n\n    // Lt specifies that this field must be less than the specified value,\n    // exclusive\n    optional fixed32 lt = 2;\n\n    // Lte specifies that this field must be less than or equal to the\n    // specified value, inclusive\n    optional fixed32 lte = 3;\n\n    // Gt specifies that this field must be greater than the specified value,\n    // exclusive. If the value of Gt is larger than a specified Lt or Lte, the\n    // range is reversed.\n    optional fixed32 gt = 4;\n\n    // Gte specifies that this field must be greater than or equal to the\n    // specified value, inclusive. If the value of Gte is larger than a\n    // specified Lt or Lte, the range is reversed.\n    optional fixed32 gte = 5;\n\n    // In specifies that this field must be equal to one of the specified\n    // values\n    repeated fixed32 in = 6;\n\n    // NotIn specifies that this field cannot be equal to one of the specified\n    // values\n    repeated fixed32 not_in = 7;\n\n    // IgnoreEmpty specifies that the validation rules of this field should be\n    // evaluated only if the field is not empty\n    optional bool ignore_empty = 8;\n}\n\n// Fixed64Rules describes the constraints applied to `fixed64` values\nmessage Fixed64Rules {\n    // Const specifies that this field must be exactly the specified value\n    optional fixed64 const = 1;\n\n    // Lt specifies that this field must be less than the specified value,\n    // exclusive\n    optional fixed64 lt = 2;\n\n    // Lte specifies that this field must be less than or equal to the\n    // specified value, inclusive\n    optional fixed64 lte = 3;\n\n    // Gt specifies that this field must be greater than the specified value,\n    // exclusive. If the value of Gt is larger than a specified Lt or Lte, the\n    // range is reversed.\n    optional fixed64 gt = 4;\n\n    // Gte specifies that this field must be greater than or equal to the\n    // specified value, inclusive. If the value of Gte is larger than a\n    // specified Lt or Lte, the range is reversed.\n    optional fixed64 gte = 5;\n\n    // In specifies that this field must be equal to one of the specified\n    // values\n    repeated fixed64 in = 6;\n\n    // NotIn specifies that this field cannot be equal to one of the specified\n    // values\n    repeated fixed64 not_in = 7;\n\n    // IgnoreEmpty specifies that the validation rules of this field should be\n    // evaluated only if the field is not empty\n    optional bool ignore_empty = 8;\n}\n\n// SFixed32Rules describes the constraints applied to `sfixed32` values\nmessage SFixed32Rules {\n    // Const specifies that this field must be exactly the specified value\n    optional sfixed32 const = 1;\n\n    // Lt specifies that this field must be less than the specified value,\n    // exclusive\n    optional sfixed32 lt = 2;\n\n    // Lte specifies that this field must be less than or equal to the\n    // specified value, inclusive\n    optional sfixed32 lte = 3;\n\n    // Gt specifies that this field must be greater than the specified value,\n    // exclusive. If the value of Gt is larger than a specified Lt or Lte, the\n    // range is reversed.\n    optional sfixed32 gt = 4;\n\n    // Gte specifies that this field must be greater than or equal to the\n    // specified value, inclusive. If the value of Gte is larger than a\n    // specified Lt or Lte, the range is reversed.\n    optional sfixed32 gte = 5;\n\n    // In specifies that this field must be equal to one of the specified\n    // values\n    repeated sfixed32 in = 6;\n\n    // NotIn specifies that this field cannot be equal to one of the specified\n    // values\n    repeated sfixed32 not_in = 7;\n\n    // IgnoreEmpty specifies that the validation rules of this field should be\n    // evaluated only if the field is not empty\n    optional bool ignore_empty = 8;\n}\n\n// SFixed64Rules describes the constraints applied to `sfixed64` values\nmessage SFixed64Rules {\n    // Const specifies that this field must be exactly the specified value\n    optional sfixed64 const = 1;\n\n    // Lt specifies that this field must be less than the specified value,\n    // exclusive\n    optional sfixed64 lt = 2;\n\n    // Lte specifies that this field must be less than or equal to the\n    // specified value, inclusive\n    optional sfixed64 lte = 3;\n\n    // Gt specifies that this field must be greater than the specified value,\n    // exclusive. If the value of Gt is larger than a specified Lt or Lte, the\n    // range is reversed.\n    optional sfixed64 gt = 4;\n\n    // Gte specifies that this field must be greater than or equal to the\n    // specified value, inclusive. If the value of Gte is larger than a\n    // specified Lt or Lte, the range is reversed.\n    optional sfixed64 gte = 5;\n\n    // In specifies that this field must be equal to one of the specified\n    // values\n    repeated sfixed64 in = 6;\n\n    // NotIn specifies that this field cannot be equal to one of the specified\n    // values\n    repeated sfixed64 not_in = 7;\n\n    // IgnoreEmpty specifies that the validation rules of this field should be\n    // evaluated only if the field is not empty\n    optional bool ignore_empty = 8;\n}\n\n// BoolRules describes the constraints applied to `bool` values\nmessage BoolRules {\n    // Const specifies that this field must be exactly the specified value\n    optional bool const = 1;\n}\n\n// StringRules describe the constraints applied to `string` values\nmessage StringRules {\n    // Const specifies that this field must be exactly the specified value\n    optional string const = 1;\n\n    // Len specifies that this field must be the specified number of\n    // characters (Unicode code points). Note that the number of\n    // characters may differ from the number of bytes in the string.\n    optional uint64 len = 19;\n\n    // MinLen specifies that this field must be the specified number of\n    // characters (Unicode code points) at a minimum. Note that the number of\n    // characters may differ from the number of bytes in the string.\n    optional uint64 min_len = 2;\n\n    // MaxLen specifies that this field must be the specified number of\n    // characters (Unicode code points) at a maximum. Note that the number of\n    // characters may differ from the number of bytes in the string.\n    optional uint64 max_len = 3;\n\n    // LenBytes specifies that this field must be the specified number of bytes\n    // at a minimum\n    optional uint64 len_bytes = 20;\n\n    // MinBytes specifies that this field must be the specified number of bytes\n    // at a minimum\n    optional uint64 min_bytes = 4;\n\n    // MaxBytes specifies that this field must be the specified number of bytes\n    // at a maximum\n    optional uint64 max_bytes = 5;\n\n    // Pattern specifies that this field must match against the specified\n    // regular expression (RE2 syntax). The included expression should elide\n    // any delimiters.\n    optional string pattern  = 6;\n\n    // Prefix specifies that this field must have the specified substring at\n    // the beginning of the string.\n    optional string prefix   = 7;\n\n    // Suffix specifies that this field must have the specified substring at\n    // the end of the string.\n    optional string suffix   = 8;\n\n    // Contains specifies that this field must have the specified substring\n    // anywhere in the string.\n    optional string contains = 9;\n\n    // NotContains specifies that this field cannot have the specified substring\n    // anywhere in the string.\n    optional string not_contains = 23;\n\n    // In specifies that this field must be equal to one of the specified\n    // values\n    repeated string in     = 10;\n\n    // NotIn specifies that this field cannot be equal to one of the specified\n    // values\n    repeated string not_in = 11;\n\n    // WellKnown rules provide advanced constraints against common string\n    // patterns\n    oneof well_known {\n        // Email specifies that the field must be a valid email address as\n        // defined by RFC 5322\n        bool email    = 12;\n\n        // Hostname specifies that the field must be a valid hostname as\n        // defined by RFC 1034. This constraint does not support\n        // internationalized domain names (IDNs).\n        bool hostname = 13;\n\n        // Ip specifies that the field must be a valid IP (v4 or v6) address.\n        // Valid IPv6 addresses should not include surrounding square brackets.\n        bool ip       = 14;\n\n        // Ipv4 specifies that the field must be a valid IPv4 address.\n        bool ipv4     = 15;\n\n        // Ipv6 specifies that the field must be a valid IPv6 address. Valid\n        // IPv6 addresses should not include surrounding square brackets.\n        bool ipv6     = 16;\n\n        // Uri specifies that the field must be a valid, absolute URI as defined\n        // by RFC 3986\n        bool uri      = 17;\n\n        // UriRef specifies that the field must be a valid URI as defined by RFC\n        // 3986 and may be relative or absolute.\n        bool uri_ref  = 18;\n\n        // Address specifies that the field must be either a valid hostname as\n        // defined by RFC 1034 (which does not support internationalized domain\n        // names or IDNs), or it can be a valid IP (v4 or v6).\n        bool address  = 21;\n\n        // Uuid specifies that the field must be a valid UUID as defined by\n        // RFC 4122\n        bool uuid     = 22;\n\n        // WellKnownRegex specifies a common well known pattern defined as a regex.\n        KnownRegex well_known_regex = 24;\n    }\n\n  // This applies to regexes HTTP_HEADER_NAME and HTTP_HEADER_VALUE to enable\n  // strict header validation.\n  // By default, this is true, and HTTP header validations are RFC-compliant.\n  // Setting to false will enable a looser validations that only disallows\n  // \\r\\n\\0 characters, which can be used to bypass header matching rules.\n  optional bool strict = 25 [default = true];\n\n  // IgnoreEmpty specifies that the validation rules of this field should be\n  // evaluated only if the field is not empty\n  optional bool ignore_empty = 26;\n}\n\n// WellKnownRegex contain some well-known patterns.\nenum KnownRegex {\n  UNKNOWN = 0;\n\n  // HTTP header name as defined by RFC 7230.\n  HTTP_HEADER_NAME = 1;\n\n  // HTTP header value as defined by RFC 7230.\n  HTTP_HEADER_VALUE = 2;\n}\n\n// BytesRules describe the constraints applied to `bytes` values\nmessage BytesRules {\n    // Const specifies that this field must be exactly the specified value\n    optional bytes const = 1;\n\n    // Len specifies that this field must be the specified number of bytes\n    optional uint64 len = 13;\n\n    // MinLen specifies that this field must be the specified number of bytes\n    // at a minimum\n    optional uint64 min_len = 2;\n\n    // MaxLen specifies that this field must be the specified number of bytes\n    // at a maximum\n    optional uint64 max_len = 3;\n\n    // Pattern specifies that this field must match against the specified\n    // regular expression (RE2 syntax). The included expression should elide\n    // any delimiters.\n    optional string pattern  = 4;\n\n    // Prefix specifies that this field must have the specified bytes at the\n    // beginning of the string.\n    optional bytes  prefix   = 5;\n\n    // Suffix specifies that this field must have the specified bytes at the\n    // end of the string.\n    optional bytes  suffix   = 6;\n\n    // Contains specifies that this field must have the specified bytes\n    // anywhere in the string.\n    optional bytes  contains = 7;\n\n    // In specifies that this field must be equal to one of the specified\n    // values\n    repeated bytes in     = 8;\n\n    // NotIn specifies that this field cannot be equal to one of the specified\n    // values\n    repeated bytes not_in = 9;\n\n    // WellKnown rules provide advanced constraints against common byte\n    // patterns\n    oneof well_known {\n        // Ip specifies that the field must be a valid IP (v4 or v6) address in\n        // byte format\n        bool ip   = 10;\n\n        // Ipv4 specifies that the field must be a valid IPv4 address in byte\n        // format\n        bool ipv4 = 11;\n\n        // Ipv6 specifies that the field must be a valid IPv6 address in byte\n        // format\n        bool ipv6 = 12;\n    }\n\n    // IgnoreEmpty specifies that the validation rules of this field should be\n    // evaluated only if the field is not empty\n    optional bool ignore_empty = 14;\n}\n\n// EnumRules describe the constraints applied to enum values\nmessage EnumRules {\n    // Const specifies that this field must be exactly the specified value\n    optional int32 const        = 1;\n\n    // DefinedOnly specifies that this field must be only one of the defined\n    // values for this enum, failing on any undefined value.\n    optional bool  defined_only = 2;\n\n    // In specifies that this field must be equal to one of the specified\n    // values\n    repeated int32 in           = 3;\n\n    // NotIn specifies that this field cannot be equal to one of the specified\n    // values\n    repeated int32 not_in       = 4;\n}\n\n// MessageRules describe the constraints applied to embedded message values.\n// For message-type fields, validation is performed recursively.\nmessage MessageRules {\n    // Skip specifies that the validation rules of this field should not be\n    // evaluated\n    optional bool skip     = 1;\n\n    // Required specifies that this field must be set\n    optional bool required = 2;\n}\n\n// RepeatedRules describe the constraints applied to `repeated` values\nmessage RepeatedRules {\n    // MinItems specifies that this field must have the specified number of\n    // items at a minimum\n    optional uint64 min_items = 1;\n\n    // MaxItems specifies that this field must have the specified number of\n    // items at a maximum\n    optional uint64 max_items = 2;\n\n    // Unique specifies that all elements in this field must be unique. This\n    // constraint is only applicable to scalar and enum types (messages are not\n    // supported).\n    optional bool   unique    = 3;\n\n    // Items specifies the constraints to be applied to each item in the field.\n    // Repeated message fields will still execute validation against each item\n    // unless skip is specified here.\n    optional FieldRules items = 4;\n\n    // IgnoreEmpty specifies that the validation rules of this field should be\n    // evaluated only if the field is not empty\n    optional bool ignore_empty = 5;\n}\n\n// MapRules describe the constraints applied to `map` values\nmessage MapRules {\n    // MinPairs specifies that this field must have the specified number of\n    // KVs at a minimum\n    optional uint64 min_pairs = 1;\n\n    // MaxPairs specifies that this field must have the specified number of\n    // KVs at a maximum\n    optional uint64 max_pairs = 2;\n\n    // NoSparse specifies values in this field cannot be unset. This only\n    // applies to map's with message value types.\n    optional bool no_sparse = 3;\n\n    // Keys specifies the constraints to be applied to each key in the field.\n    optional FieldRules keys   = 4;\n\n    // Values specifies the constraints to be applied to the value of each key\n    // in the field. Message values will still have their validations evaluated\n    // unless skip is specified here.\n    optional FieldRules values = 5;\n\n    // IgnoreEmpty specifies that the validation rules of this field should be\n    // evaluated only if the field is not empty\n    optional bool ignore_empty = 6;\n}\n\n// AnyRules describe constraints applied exclusively to the\n// `google.protobuf.Any` well-known type\nmessage AnyRules {\n    // Required specifies that this field must be set\n    optional bool required = 1;\n\n    // In specifies that this field's `type_url` must be equal to one of the\n    // specified values.\n    repeated string in     = 2;\n\n    // NotIn specifies that this field's `type_url` must not be equal to any of\n    // the specified values.\n    repeated string not_in = 3;\n}\n\n// DurationRules describe the constraints applied exclusively to the\n// `google.protobuf.Duration` well-known type\nmessage DurationRules {\n    // Required specifies that this field must be set\n    optional bool required = 1;\n\n    // Const specifies that this field must be exactly the specified value\n    optional google.protobuf.Duration const = 2;\n\n    // Lt specifies that this field must be less than the specified value,\n    // exclusive\n    optional google.protobuf.Duration lt = 3;\n\n    // Lt specifies that this field must be less than the specified value,\n    // inclusive\n    optional google.protobuf.Duration lte = 4;\n\n    // Gt specifies that this field must be greater than the specified value,\n    // exclusive\n    optional google.protobuf.Duration gt = 5;\n\n    // Gte specifies that this field must be greater than the specified value,\n    // inclusive\n    optional google.protobuf.Duration gte = 6;\n\n    // In specifies that this field must be equal to one of the specified\n    // values\n    repeated google.protobuf.Duration in = 7;\n\n    // NotIn specifies that this field cannot be equal to one of the specified\n    // values\n    repeated google.protobuf.Duration not_in = 8;\n}\n\n// TimestampRules describe the constraints applied exclusively to the\n// `google.protobuf.Timestamp` well-known type\nmessage TimestampRules {\n    // Required specifies that this field must be set\n    optional bool required = 1;\n\n    // Const specifies that this field must be exactly the specified value\n    optional google.protobuf.Timestamp const = 2;\n\n    // Lt specifies that this field must be less than the specified value,\n    // exclusive\n    optional google.protobuf.Timestamp lt = 3;\n\n    // Lte specifies that this field must be less than the specified value,\n    // inclusive\n    optional google.protobuf.Timestamp lte = 4;\n\n    // Gt specifies that this field must be greater than the specified value,\n    // exclusive\n    optional google.protobuf.Timestamp gt = 5;\n\n    // Gte specifies that this field must be greater than the specified value,\n    // inclusive\n    optional google.protobuf.Timestamp gte = 6;\n\n    // LtNow specifies that this must be less than the current time. LtNow\n    // can only be used with the Within rule.\n    optional bool lt_now  = 7;\n\n    // GtNow specifies that this must be greater than the current time. GtNow\n    // can only be used with the Within rule.\n    optional bool gt_now  = 8;\n\n    // Within specifies that this field must be within this duration of the\n    // current time. This constraint can be used alone or with the LtNow and\n    // GtNow rules.\n    optional google.protobuf.Duration within = 9;\n}\n"
  },
  {
    "path": "transport/grpc/balancer.go",
    "content": "package grpc\n\nimport (\n\t\"google.golang.org/grpc/balancer\"\n\t\"google.golang.org/grpc/balancer/base\"\n\t\"google.golang.org/grpc/metadata\"\n\n\t\"github.com/go-kratos/kratos/v2/registry\"\n\t\"github.com/go-kratos/kratos/v2/selector\"\n\t\"github.com/go-kratos/kratos/v2/transport\"\n)\n\nconst (\n\tbalancerName = \"selector\"\n)\n\nvar (\n\t_ base.PickerBuilder = (*balancerBuilder)(nil)\n\t_ balancer.Picker    = (*balancerPicker)(nil)\n)\n\nfunc init() {\n\tb := base.NewBalancerBuilder(\n\t\tbalancerName,\n\t\t&balancerBuilder{\n\t\t\tbuilder: selector.GlobalSelector(),\n\t\t},\n\t\tbase.Config{HealthCheck: true},\n\t)\n\tbalancer.Register(b)\n}\n\ntype balancerBuilder struct {\n\tbuilder selector.Builder\n}\n\n// Build creates a grpc Picker.\nfunc (b *balancerBuilder) Build(info base.PickerBuildInfo) balancer.Picker {\n\tif len(info.ReadySCs) == 0 {\n\t\t// Block the RPC until a new picker is available via UpdateState().\n\t\treturn base.NewErrPicker(balancer.ErrNoSubConnAvailable)\n\t}\n\tnodes := make([]selector.Node, 0, len(info.ReadySCs))\n\tfor conn, info := range info.ReadySCs {\n\t\tins, _ := info.Address.Attributes.Value(\"rawServiceInstance\").(*registry.ServiceInstance)\n\t\tnodes = append(nodes, &grpcNode{\n\t\t\tNode:    selector.NewNode(\"grpc\", info.Address.Addr, ins),\n\t\t\tsubConn: conn,\n\t\t})\n\t}\n\tp := &balancerPicker{\n\t\tselector: b.builder.Build(),\n\t}\n\tp.selector.Apply(nodes)\n\treturn p\n}\n\n// balancerPicker is a grpc picker.\ntype balancerPicker struct {\n\tselector selector.Selector\n}\n\n// Pick pick instances.\nfunc (p *balancerPicker) Pick(info balancer.PickInfo) (balancer.PickResult, error) {\n\tvar filters []selector.NodeFilter\n\tif tr, ok := transport.FromClientContext(info.Ctx); ok {\n\t\tif gtr, ok := tr.(*Transport); ok {\n\t\t\tfilters = gtr.NodeFilters()\n\t\t}\n\t}\n\n\tn, done, err := p.selector.Select(info.Ctx, selector.WithNodeFilter(filters...))\n\tif err != nil {\n\t\treturn balancer.PickResult{}, err\n\t}\n\n\treturn balancer.PickResult{\n\t\tSubConn: n.(*grpcNode).subConn,\n\t\tDone: func(di balancer.DoneInfo) {\n\t\t\tdone(info.Ctx, selector.DoneInfo{\n\t\t\t\tErr:           di.Err,\n\t\t\t\tBytesSent:     di.BytesSent,\n\t\t\t\tBytesReceived: di.BytesReceived,\n\t\t\t\tReplyMD:       Trailer(di.Trailer),\n\t\t\t})\n\t\t},\n\t}, nil\n}\n\n// Trailer is a grpc trailer MD.\ntype Trailer metadata.MD\n\n// Get get a grpc trailer value.\nfunc (t Trailer) Get(k string) string {\n\tv := metadata.MD(t).Get(k)\n\tif len(v) > 0 {\n\t\treturn v[0]\n\t}\n\treturn \"\"\n}\n\ntype grpcNode struct {\n\tselector.Node\n\tsubConn balancer.SubConn\n}\n"
  },
  {
    "path": "transport/grpc/balancer_test.go",
    "content": "package grpc\n\nimport (\n\t\"context\"\n\t\"reflect\"\n\t\"testing\"\n\n\t\"google.golang.org/grpc/metadata\"\n\n\t\"github.com/go-kratos/kratos/v2/selector\"\n)\n\nfunc TestTrailer(t *testing.T) {\n\ttrailer := Trailer(metadata.New(map[string]string{\"a\": \"b\"}))\n\tif !reflect.DeepEqual(\"b\", trailer.Get(\"a\")) {\n\t\tt.Errorf(\"expect %v, got %v\", \"b\", trailer.Get(\"a\"))\n\t}\n\tif !reflect.DeepEqual(\"\", trailer.Get(\"notfound\")) {\n\t\tt.Errorf(\"expect %v, got %v\", \"\", trailer.Get(\"notfound\"))\n\t}\n}\n\nfunc TestFilters(t *testing.T) {\n\to := &clientOptions{}\n\n\tWithNodeFilter(func(_ context.Context, nodes []selector.Node) []selector.Node {\n\t\treturn nodes\n\t})(o)\n\tif !reflect.DeepEqual(1, len(o.filters)) {\n\t\tt.Errorf(\"expect %v, got %v\", 1, len(o.filters))\n\t}\n}\n"
  },
  {
    "path": "transport/grpc/client.go",
    "content": "package grpc\n\nimport (\n\t\"context\"\n\t\"crypto/tls\"\n\t\"fmt\"\n\t\"time\"\n\n\t\"google.golang.org/grpc\"\n\t\"google.golang.org/grpc/credentials\"\n\tgrpcinsecure \"google.golang.org/grpc/credentials/insecure\"\n\tgrpcmd \"google.golang.org/grpc/metadata\"\n\n\t\"github.com/go-kratos/kratos/v2/internal/matcher\"\n\t\"github.com/go-kratos/kratos/v2/log\"\n\t\"github.com/go-kratos/kratos/v2/middleware\"\n\t\"github.com/go-kratos/kratos/v2/registry\"\n\t\"github.com/go-kratos/kratos/v2/selector\"\n\t\"github.com/go-kratos/kratos/v2/selector/wrr\"\n\t\"github.com/go-kratos/kratos/v2/transport\"\n\t\"github.com/go-kratos/kratos/v2/transport/grpc/resolver/discovery\"\n\n\t// init resolver\n\t_ \"github.com/go-kratos/kratos/v2/transport/grpc/resolver/direct\"\n)\n\nfunc init() {\n\tif selector.GlobalSelector() == nil {\n\t\tselector.SetGlobalSelector(wrr.NewBuilder())\n\t}\n}\n\n// ClientOption is gRPC client option.\ntype ClientOption func(o *clientOptions)\n\n// WithEndpoint with client endpoint.\nfunc WithEndpoint(endpoint string) ClientOption {\n\treturn func(o *clientOptions) {\n\t\to.endpoint = endpoint\n\t}\n}\n\n// WithSubset with client discovery subset size.\n// zero value means subset filter disabled\nfunc WithSubset(size int) ClientOption {\n\treturn func(o *clientOptions) {\n\t\to.subsetSize = size\n\t}\n}\n\n// WithTimeout with client timeout.\nfunc WithTimeout(timeout time.Duration) ClientOption {\n\treturn func(o *clientOptions) {\n\t\to.timeout = timeout\n\t}\n}\n\n// WithMiddleware with client middleware.\nfunc WithMiddleware(m ...middleware.Middleware) ClientOption {\n\treturn func(o *clientOptions) {\n\t\to.middleware = m\n\t}\n}\n\n// WithStreamMiddleware with client stream middleware.\nfunc WithStreamMiddleware(m ...middleware.Middleware) ClientOption {\n\treturn func(o *clientOptions) {\n\t\to.streamMiddleware = m\n\t}\n}\n\n// WithDiscovery with client discovery.\nfunc WithDiscovery(d registry.Discovery) ClientOption {\n\treturn func(o *clientOptions) {\n\t\to.discovery = d\n\t}\n}\n\n// WithTLSConfig with TLS config.\nfunc WithTLSConfig(c *tls.Config) ClientOption {\n\treturn func(o *clientOptions) {\n\t\to.tlsConf = c\n\t}\n}\n\n// WithUnaryInterceptor returns a DialOption that specifies the interceptor for unary RPCs.\nfunc WithUnaryInterceptor(in ...grpc.UnaryClientInterceptor) ClientOption {\n\treturn func(o *clientOptions) {\n\t\to.ints = in\n\t}\n}\n\n// WithStreamInterceptor returns a DialOption that specifies the interceptor for streaming RPCs.\nfunc WithStreamInterceptor(in ...grpc.StreamClientInterceptor) ClientOption {\n\treturn func(o *clientOptions) {\n\t\to.streamInts = in\n\t}\n}\n\n// WithOptions with gRPC options.\nfunc WithOptions(opts ...grpc.DialOption) ClientOption {\n\treturn func(o *clientOptions) {\n\t\to.grpcOpts = opts\n\t}\n}\n\n// WithNodeFilter with select filters\nfunc WithNodeFilter(filters ...selector.NodeFilter) ClientOption {\n\treturn func(o *clientOptions) {\n\t\to.filters = filters\n\t}\n}\n\n// WithHealthCheck with health check\nfunc WithHealthCheck(healthCheck bool) ClientOption {\n\treturn func(o *clientOptions) {\n\t\tif !healthCheck {\n\t\t\to.healthCheckConfig = \"\"\n\t\t}\n\t}\n}\n\n// WithLogger with logger\n// Deprecated: use global logger instead.\nfunc WithLogger(log.Logger) ClientOption {\n\treturn func(*clientOptions) {}\n}\n\nfunc WithPrintDiscoveryDebugLog(p bool) ClientOption {\n\treturn func(o *clientOptions) {\n\t\to.printDiscoveryDebugLog = p\n\t}\n}\n\n// clientOptions is gRPC Client\ntype clientOptions struct {\n\tendpoint               string\n\tsubsetSize             int\n\ttlsConf                *tls.Config\n\ttimeout                time.Duration\n\tdiscovery              registry.Discovery\n\tmiddleware             []middleware.Middleware\n\tstreamMiddleware       []middleware.Middleware\n\tints                   []grpc.UnaryClientInterceptor\n\tstreamInts             []grpc.StreamClientInterceptor\n\tgrpcOpts               []grpc.DialOption\n\tbalancerName           string\n\tfilters                []selector.NodeFilter\n\thealthCheckConfig      string\n\tprintDiscoveryDebugLog bool\n}\n\n// Dial returns a GRPC connection.\nfunc Dial(ctx context.Context, opts ...ClientOption) (*grpc.ClientConn, error) {\n\treturn dial(ctx, false, opts...)\n}\n\n// DialInsecure returns an insecure GRPC connection.\nfunc DialInsecure(ctx context.Context, opts ...ClientOption) (*grpc.ClientConn, error) {\n\treturn dial(ctx, true, opts...)\n}\n\nfunc dial(ctx context.Context, insecure bool, opts ...ClientOption) (*grpc.ClientConn, error) {\n\toptions := clientOptions{\n\t\ttimeout:                2000 * time.Millisecond,\n\t\tbalancerName:           balancerName,\n\t\tsubsetSize:             25,\n\t\tprintDiscoveryDebugLog: true,\n\t\thealthCheckConfig:      `,\"healthCheckConfig\":{\"serviceName\":\"\"}`,\n\t}\n\tfor _, o := range opts {\n\t\to(&options)\n\t}\n\tints := []grpc.UnaryClientInterceptor{\n\t\tunaryClientInterceptor(options.middleware, options.timeout, options.filters),\n\t}\n\tsints := []grpc.StreamClientInterceptor{\n\t\tstreamClientInterceptor(options.streamMiddleware, options.filters),\n\t}\n\n\tif len(options.ints) > 0 {\n\t\tints = append(ints, options.ints...)\n\t}\n\tif len(options.streamInts) > 0 {\n\t\tsints = append(sints, options.streamInts...)\n\t}\n\tgrpcOpts := []grpc.DialOption{\n\t\tgrpc.WithDefaultServiceConfig(fmt.Sprintf(`{\"loadBalancingConfig\": [{\"%s\":{}}]%s}`,\n\t\t\toptions.balancerName, options.healthCheckConfig)),\n\t\tgrpc.WithChainUnaryInterceptor(ints...),\n\t\tgrpc.WithChainStreamInterceptor(sints...),\n\t}\n\n\tif options.discovery != nil {\n\t\tgrpcOpts = append(grpcOpts,\n\t\t\tgrpc.WithResolvers(\n\t\t\t\tdiscovery.NewBuilder(\n\t\t\t\t\toptions.discovery,\n\t\t\t\t\tdiscovery.WithInsecure(insecure),\n\t\t\t\t\tdiscovery.WithTimeout(options.timeout),\n\t\t\t\t\tdiscovery.WithSubset(options.subsetSize),\n\t\t\t\t\tdiscovery.PrintDebugLog(options.printDiscoveryDebugLog),\n\t\t\t\t)))\n\t}\n\tif insecure {\n\t\tgrpcOpts = append(grpcOpts, grpc.WithTransportCredentials(grpcinsecure.NewCredentials()))\n\t}\n\tif options.tlsConf != nil {\n\t\tgrpcOpts = append(grpcOpts, grpc.WithTransportCredentials(credentials.NewTLS(options.tlsConf)))\n\t}\n\tif len(options.grpcOpts) > 0 {\n\t\tgrpcOpts = append(grpcOpts, options.grpcOpts...)\n\t}\n\treturn grpc.DialContext(ctx, options.endpoint, grpcOpts...)\n}\n\nfunc unaryClientInterceptor(ms []middleware.Middleware, timeout time.Duration, filters []selector.NodeFilter) grpc.UnaryClientInterceptor {\n\treturn func(ctx context.Context, method string, req, reply any, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error {\n\t\tctx = transport.NewClientContext(ctx, &Transport{\n\t\t\tendpoint:    cc.Target(),\n\t\t\toperation:   method,\n\t\t\treqHeader:   headerCarrier{},\n\t\t\tnodeFilters: filters,\n\t\t})\n\t\tif timeout > 0 {\n\t\t\tvar cancel context.CancelFunc\n\t\t\tctx, cancel = context.WithTimeout(ctx, timeout)\n\t\t\tdefer cancel()\n\t\t}\n\t\th := func(ctx context.Context, req any) (any, error) {\n\t\t\tif tr, ok := transport.FromClientContext(ctx); ok {\n\t\t\t\theader := tr.RequestHeader()\n\t\t\t\tkeys := header.Keys()\n\t\t\t\tkeyvals := make([]string, 0, len(keys))\n\t\t\t\tfor _, k := range keys {\n\t\t\t\t\tkeyvals = append(keyvals, k, header.Get(k))\n\t\t\t\t}\n\t\t\t\tctx = grpcmd.AppendToOutgoingContext(ctx, keyvals...)\n\t\t\t}\n\t\t\treturn reply, invoker(ctx, method, req, reply, cc, opts...)\n\t\t}\n\t\tif len(ms) > 0 {\n\t\t\th = middleware.Chain(ms...)(h)\n\t\t}\n\t\tvar p selector.Peer\n\t\tctx = selector.NewPeerContext(ctx, &p)\n\t\t_, err := h(ctx, req)\n\t\treturn err\n\t}\n}\n\n// wrappedClientStream wraps the grpc.ClientStream and applies middleware\ntype wrappedClientStream struct {\n\tgrpc.ClientStream\n\tctx        context.Context\n\tmiddleware matcher.Matcher\n}\n\nfunc (w *wrappedClientStream) Context() context.Context {\n\treturn w.ctx\n}\n\nfunc (w *wrappedClientStream) SendMsg(m any) error {\n\th := func(_ context.Context, req any) (any, error) {\n\t\treturn req, w.ClientStream.SendMsg(m)\n\t}\n\n\tinfo, ok := transport.FromClientContext(w.ctx)\n\tif !ok {\n\t\treturn fmt.Errorf(\"transport value stored in ctx returns: %v\", ok)\n\t}\n\n\tif next := w.middleware.Match(info.Operation()); len(next) > 0 {\n\t\th = middleware.Chain(next...)(h)\n\t}\n\n\t_, err := h(w.ctx, m)\n\treturn err\n}\n\nfunc (w *wrappedClientStream) RecvMsg(m any) error {\n\th := func(_ context.Context, req any) (any, error) {\n\t\treturn req, w.ClientStream.RecvMsg(m)\n\t}\n\n\tinfo, ok := transport.FromClientContext(w.ctx)\n\tif !ok {\n\t\treturn fmt.Errorf(\"transport value stored in ctx returns: %v\", ok)\n\t}\n\n\tif next := w.middleware.Match(info.Operation()); len(next) > 0 {\n\t\th = middleware.Chain(next...)(h)\n\t}\n\n\t_, err := h(w.ctx, m)\n\treturn err\n}\n\nfunc streamClientInterceptor(ms []middleware.Middleware, filters []selector.NodeFilter) grpc.StreamClientInterceptor {\n\treturn func(ctx context.Context, desc *grpc.StreamDesc, cc *grpc.ClientConn, method string, streamer grpc.Streamer, opts ...grpc.CallOption) (grpc.ClientStream, error) { // nolint\n\t\tctx = transport.NewClientContext(ctx, &Transport{\n\t\t\tendpoint:    cc.Target(),\n\t\t\toperation:   method,\n\t\t\treqHeader:   headerCarrier{},\n\t\t\tnodeFilters: filters,\n\t\t})\n\t\tvar p selector.Peer\n\t\tctx = selector.NewPeerContext(ctx, &p)\n\n\t\tclientStream, err := streamer(ctx, desc, cc, method, opts...)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\th := func(_ context.Context, _ any) (any, error) {\n\t\t\treturn streamer, nil\n\t\t}\n\n\t\tm := matcher.New()\n\t\tif len(ms) > 0 {\n\t\t\tm.Use(ms...)\n\t\t\tmiddleware.Chain(ms...)(h)\n\t\t}\n\n\t\twrappedStream := &wrappedClientStream{\n\t\t\tClientStream: clientStream,\n\t\t\tctx:          ctx,\n\t\t\tmiddleware:   m,\n\t\t}\n\n\t\treturn wrappedStream, nil\n\t}\n}\n"
  },
  {
    "path": "transport/grpc/client_test.go",
    "content": "package grpc\n\nimport (\n\t\"context\"\n\t\"crypto/tls\"\n\t\"reflect\"\n\t\"testing\"\n\t\"time\"\n\n\t\"google.golang.org/grpc\"\n\n\t\"github.com/go-kratos/kratos/v2/middleware\"\n\t\"github.com/go-kratos/kratos/v2/registry\"\n)\n\nfunc TestWithEndpoint(t *testing.T) {\n\to := &clientOptions{}\n\tv := \"abc\"\n\tWithEndpoint(v)(o)\n\tif !reflect.DeepEqual(v, o.endpoint) {\n\t\tt.Errorf(\"expect %v but got %v\", v, o.endpoint)\n\t}\n}\n\nfunc TestWithTimeout(t *testing.T) {\n\to := &clientOptions{}\n\tv := time.Duration(123)\n\tWithTimeout(v)(o)\n\tif !reflect.DeepEqual(v, o.timeout) {\n\t\tt.Errorf(\"expect %v but got %v\", v, o.timeout)\n\t}\n}\n\nfunc TestWithMiddleware(t *testing.T) {\n\to := &clientOptions{}\n\tv := []middleware.Middleware{\n\t\tfunc(middleware.Handler) middleware.Handler { return nil },\n\t}\n\tWithMiddleware(v...)(o)\n\tif !reflect.DeepEqual(v, o.middleware) {\n\t\tt.Errorf(\"expect %v but got %v\", v, o.middleware)\n\t}\n}\n\nfunc TestWithStreamMiddleware(t *testing.T) {\n\to := &clientOptions{}\n\tv := []middleware.Middleware{\n\t\tfunc(middleware.Handler) middleware.Handler { return nil },\n\t}\n\tWithStreamMiddleware(v...)(o)\n\tif !reflect.DeepEqual(v, o.streamMiddleware) {\n\t\tt.Errorf(\"expect %v but got %v\", v, o.streamInts)\n\t}\n}\n\ntype mockRegistry struct{}\n\nfunc (m *mockRegistry) GetService(_ context.Context, _ string) ([]*registry.ServiceInstance, error) {\n\treturn nil, nil\n}\n\nfunc (m *mockRegistry) Watch(_ context.Context, _ string) (registry.Watcher, error) {\n\treturn nil, nil\n}\n\nfunc TestWithDiscovery(t *testing.T) {\n\to := &clientOptions{}\n\tv := &mockRegistry{}\n\tWithDiscovery(v)(o)\n\tif !reflect.DeepEqual(v, o.discovery) {\n\t\tt.Errorf(\"expect %v but got %v\", v, o.discovery)\n\t}\n}\n\nfunc TestWithTLSConfig(t *testing.T) {\n\to := &clientOptions{}\n\tv := &tls.Config{}\n\tWithTLSConfig(v)(o)\n\tif !reflect.DeepEqual(v, o.tlsConf) {\n\t\tt.Errorf(\"expect %v but got %v\", v, o.tlsConf)\n\t}\n}\n\nfunc EmptyMiddleware() middleware.Middleware {\n\treturn func(handler middleware.Handler) middleware.Handler {\n\t\treturn func(ctx context.Context, req any) (reply any, err error) {\n\t\t\treturn handler(ctx, req)\n\t\t}\n\t}\n}\n\nfunc TestUnaryClientInterceptor(t *testing.T) {\n\tf := unaryClientInterceptor([]middleware.Middleware{EmptyMiddleware()}, time.Duration(100), nil)\n\treq := &struct{}{}\n\tresp := &struct{}{}\n\n\terr := f(context.TODO(), \"hello\", req, resp, &grpc.ClientConn{},\n\t\tfunc(context.Context, string, any, any, *grpc.ClientConn, ...grpc.CallOption) error {\n\t\t\treturn nil\n\t\t})\n\tif err != nil {\n\t\tt.Errorf(\"unexpected error: %v\", err)\n\t}\n}\n\nfunc TestWithUnaryInterceptor(t *testing.T) {\n\to := &clientOptions{}\n\tv := []grpc.UnaryClientInterceptor{\n\t\tfunc(context.Context, string, any, any, *grpc.ClientConn, grpc.UnaryInvoker, ...grpc.CallOption) error {\n\t\t\treturn nil\n\t\t},\n\t\tfunc(context.Context, string, any, any, *grpc.ClientConn, grpc.UnaryInvoker, ...grpc.CallOption) error {\n\t\t\treturn nil\n\t\t},\n\t}\n\tWithUnaryInterceptor(v...)(o)\n\tif !reflect.DeepEqual(v, o.ints) {\n\t\tt.Errorf(\"expect %v but got %v\", v, o.ints)\n\t}\n}\n\nfunc TestWithOptions(t *testing.T) {\n\to := &clientOptions{}\n\tv := []grpc.DialOption{\n\t\tgrpc.EmptyDialOption{},\n\t}\n\tWithOptions(v...)(o)\n\tif !reflect.DeepEqual(v, o.grpcOpts) {\n\t\tt.Errorf(\"expect %v but got %v\", v, o.grpcOpts)\n\t}\n}\n\nfunc TestWithHealthCheck(t *testing.T) {\n\to := &clientOptions{\n\t\thealthCheckConfig: `,\"healthCheckConfig\":{\"serviceName\":\"\"}`,\n\t}\n\tWithHealthCheck(false)(o)\n\tif !reflect.DeepEqual(\"\", o.healthCheckConfig) {\n\t\tt.Errorf(\"expect %v but got %v\", \"\", o.healthCheckConfig)\n\t}\n}\n\nfunc TestDial(t *testing.T) {\n\to := &clientOptions{}\n\tv := []grpc.DialOption{\n\t\tgrpc.EmptyDialOption{},\n\t}\n\tWithOptions(v...)(o)\n\tif !reflect.DeepEqual(v, o.grpcOpts) {\n\t\tt.Errorf(\"expect %v but got %v\", v, o.grpcOpts)\n\t}\n}\n\nfunc TestDialConn(t *testing.T) {\n\t_, err := dial(\n\t\tcontext.Background(),\n\t\ttrue,\n\t\tWithDiscovery(&mockRegistry{}),\n\t\tWithTimeout(10*time.Second),\n\t\tWithEndpoint(\"abc\"),\n\t\tWithMiddleware(EmptyMiddleware()),\n\t\tWithStreamMiddleware(EmptyMiddleware()),\n\t)\n\tif err != nil {\n\t\tt.Error(err)\n\t}\n}\n"
  },
  {
    "path": "transport/grpc/codec.go",
    "content": "package grpc\n\nimport (\n\t\"fmt\"\n\n\t\"google.golang.org/grpc/encoding\"\n\t\"google.golang.org/protobuf/proto\"\n\n\tenc \"github.com/go-kratos/kratos/v2/encoding\"\n\t\"github.com/go-kratos/kratos/v2/encoding/json\"\n)\n\nfunc init() {\n\tencoding.RegisterCodec(codec{})\n}\n\n// codec is a Codec implementation with protobuf. It is the default codec for gRPC.\ntype codec struct{}\n\nfunc (codec) Marshal(v any) ([]byte, error) {\n\tvv, ok := v.(proto.Message)\n\tif !ok {\n\t\treturn nil, fmt.Errorf(\"failed to marshal, message is %T, want proto.Message\", v)\n\t}\n\treturn enc.GetCodec(json.Name).Marshal(vv)\n}\n\nfunc (codec) Unmarshal(data []byte, v any) error {\n\tvv, ok := v.(proto.Message)\n\tif !ok {\n\t\treturn fmt.Errorf(\"failed to unmarshal, message is %T, want proto.Message\", v)\n\t}\n\treturn enc.GetCodec(json.Name).Unmarshal(data, vv)\n}\n\nfunc (codec) Name() string {\n\treturn json.Name\n}\n"
  },
  {
    "path": "transport/grpc/codec_test.go",
    "content": "package grpc\n\nimport (\n\t\"reflect\"\n\t\"testing\"\n\n\t\"google.golang.org/protobuf/types/known/structpb\"\n)\n\nfunc TestCodec(t *testing.T) {\n\tin, err := structpb.NewStruct(map[string]any{\"Golang\": \"Kratos\"})\n\tif err != nil {\n\t\tt.Errorf(\"grpc codec create input data error:%v\", err)\n\t}\n\tc := codec{}\n\tdata, err := c.Marshal(in)\n\tif err != nil {\n\t\tt.Errorf(\"grpc codec marshal error:%v\", err)\n\t}\n\tout := &structpb.Struct{}\n\terr = c.Unmarshal(data, out)\n\tif err != nil {\n\t\tt.Errorf(\"grpc codec unmarshal error:%v\", err)\n\t}\n\tif !reflect.DeepEqual(in, out) {\n\t\tt.Errorf(\"grpc codec want %v, got %v\", in, out)\n\t}\n}\n"
  },
  {
    "path": "transport/grpc/interceptor.go",
    "content": "package grpc\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"google.golang.org/grpc\"\n\tgrpcmd \"google.golang.org/grpc/metadata\"\n\n\tic \"github.com/go-kratos/kratos/v2/internal/context\"\n\t\"github.com/go-kratos/kratos/v2/internal/matcher\"\n\t\"github.com/go-kratos/kratos/v2/middleware\"\n\t\"github.com/go-kratos/kratos/v2/transport\"\n)\n\n// unaryServerInterceptor is a gRPC unary server interceptor\nfunc (s *Server) unaryServerInterceptor() grpc.UnaryServerInterceptor {\n\treturn func(ctx context.Context, req any, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (any, error) {\n\t\tctx, cancel := ic.Merge(ctx, s.baseCtx)\n\t\tdefer cancel()\n\t\tmd, _ := grpcmd.FromIncomingContext(ctx)\n\t\treplyHeader := grpcmd.MD{}\n\t\ttr := &Transport{\n\t\t\toperation:   info.FullMethod,\n\t\t\treqHeader:   headerCarrier(md),\n\t\t\treplyHeader: headerCarrier(replyHeader),\n\t\t}\n\t\tif s.endpoint != nil {\n\t\t\ttr.endpoint = s.endpoint.String()\n\t\t}\n\t\tctx = transport.NewServerContext(ctx, tr)\n\t\tif s.timeout > 0 {\n\t\t\tctx, cancel = context.WithTimeout(ctx, s.timeout)\n\t\t\tdefer cancel()\n\t\t}\n\t\th := func(ctx context.Context, req any) (any, error) {\n\t\t\treturn handler(ctx, req)\n\t\t}\n\t\tif next := s.middleware.Match(tr.Operation()); len(next) > 0 {\n\t\t\th = middleware.Chain(next...)(h)\n\t\t}\n\t\treply, err := h(ctx, req)\n\t\tif len(replyHeader) > 0 {\n\t\t\t_ = grpc.SetHeader(ctx, replyHeader)\n\t\t}\n\t\treturn reply, err\n\t}\n}\n\n// wrappedStream is rewrite grpc stream's context\ntype wrappedStream struct {\n\tgrpc.ServerStream\n\tctx        context.Context\n\tmiddleware matcher.Matcher\n}\n\nfunc NewWrappedStream(ctx context.Context, stream grpc.ServerStream, m matcher.Matcher) grpc.ServerStream {\n\treturn &wrappedStream{\n\t\tServerStream: stream,\n\t\tctx:          ctx,\n\t\tmiddleware:   m,\n\t}\n}\n\nfunc (w *wrappedStream) Context() context.Context {\n\treturn w.ctx\n}\n\n// streamServerInterceptor is a gRPC stream server interceptor\nfunc (s *Server) streamServerInterceptor() grpc.StreamServerInterceptor {\n\treturn func(srv any, ss grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error {\n\t\tctx, cancel := ic.Merge(ss.Context(), s.baseCtx)\n\t\tdefer cancel()\n\t\tmd, _ := grpcmd.FromIncomingContext(ctx)\n\t\treplyHeader := grpcmd.MD{}\n\t\tctx = transport.NewServerContext(ctx, &Transport{\n\t\t\tendpoint:    s.endpoint.String(),\n\t\t\toperation:   info.FullMethod,\n\t\t\treqHeader:   headerCarrier(md),\n\t\t\treplyHeader: headerCarrier(replyHeader),\n\t\t})\n\n\t\th := func(_ context.Context, _ any) (any, error) {\n\t\t\treturn handler(srv, ss), nil\n\t\t}\n\n\t\tif next := s.streamMiddleware.Match(info.FullMethod); len(next) > 0 {\n\t\t\tmiddleware.Chain(next...)(h)\n\t\t}\n\n\t\tctx = context.WithValue(ctx, stream{\n\t\t\tServerStream:     ss,\n\t\t\tstreamMiddleware: s.streamMiddleware,\n\t\t}, ss)\n\t\tws := NewWrappedStream(ctx, ss, s.streamMiddleware)\n\n\t\terr := handler(srv, ws)\n\t\tif len(replyHeader) > 0 {\n\t\t\t_ = grpc.SetHeader(ctx, replyHeader)\n\t\t}\n\t\treturn err\n\t}\n}\n\ntype stream struct {\n\tgrpc.ServerStream\n\tstreamMiddleware matcher.Matcher\n}\n\nfunc GetStream(ctx context.Context) grpc.ServerStream {\n\treturn ctx.Value(stream{}).(grpc.ServerStream)\n}\n\nfunc (w *wrappedStream) SendMsg(m any) error {\n\th := func(_ context.Context, req any) (any, error) {\n\t\treturn req, w.ServerStream.SendMsg(m)\n\t}\n\n\tinfo, ok := transport.FromServerContext(w.ctx)\n\tif !ok {\n\t\treturn fmt.Errorf(\"transport value stored in ctx returns: %v\", ok)\n\t}\n\n\tif next := w.middleware.Match(info.Operation()); len(next) > 0 {\n\t\th = middleware.Chain(next...)(h)\n\t}\n\n\t_, err := h(w.ctx, m)\n\treturn err\n}\n\nfunc (w *wrappedStream) RecvMsg(m any) error {\n\th := func(_ context.Context, req any) (any, error) {\n\t\treturn req, w.ServerStream.RecvMsg(m)\n\t}\n\n\tinfo, ok := transport.FromServerContext(w.ctx)\n\tif !ok {\n\t\treturn fmt.Errorf(\"transport value stored in ctx returns: %v\", ok)\n\t}\n\n\tif next := w.middleware.Match(info.Operation()); len(next) > 0 {\n\t\th = middleware.Chain(next...)(h)\n\t}\n\n\t_, err := h(w.ctx, m)\n\treturn err\n}\n"
  },
  {
    "path": "transport/grpc/resolver/direct/builder.go",
    "content": "package direct\n\nimport (\n\t\"strings\"\n\n\t\"google.golang.org/grpc/resolver\"\n)\n\nconst name = \"direct\"\n\nfunc init() {\n\tresolver.Register(NewBuilder())\n}\n\ntype directBuilder struct{}\n\n// NewBuilder creates a directBuilder which is used to factory direct resolvers.\n// example:\n//\n//\tdirect://<authority>/127.0.0.1:9000,127.0.0.2:9000\nfunc NewBuilder() resolver.Builder {\n\treturn &directBuilder{}\n}\n\nfunc (d *directBuilder) Build(target resolver.Target, cc resolver.ClientConn, _ resolver.BuildOptions) (resolver.Resolver, error) {\n\taddrs := make([]resolver.Address, 0)\n\tfor _, addr := range strings.Split(strings.TrimPrefix(target.URL.Path, \"/\"), \",\") {\n\t\taddrs = append(addrs, resolver.Address{Addr: addr})\n\t}\n\terr := cc.UpdateState(resolver.State{\n\t\tAddresses: addrs,\n\t})\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn newDirectResolver(), nil\n}\n\nfunc (d *directBuilder) Scheme() string {\n\treturn name\n}\n"
  },
  {
    "path": "transport/grpc/resolver/direct/builder_test.go",
    "content": "package direct\n\nimport (\n\t\"errors\"\n\t\"reflect\"\n\t\"testing\"\n\n\t\"google.golang.org/grpc/resolver\"\n\t\"google.golang.org/grpc/serviceconfig\"\n)\n\nfunc TestDirectBuilder_Scheme(t *testing.T) {\n\tb := NewBuilder()\n\tif !reflect.DeepEqual(b.Scheme(), \"direct\") {\n\t\tt.Errorf(\"expect %v, got %v\", \"direct\", b.Scheme())\n\t}\n}\n\ntype mockConn struct {\n\tneedUpdateStateErr bool\n}\n\nfunc (m *mockConn) UpdateState(resolver.State) error {\n\tif m.needUpdateStateErr {\n\t\treturn errors.New(\"mock test needUpdateStateErr\")\n\t}\n\treturn nil\n}\n\nfunc (m *mockConn) ReportError(error) {}\n\nfunc (m *mockConn) NewAddress(_ []resolver.Address) {}\n\nfunc (m *mockConn) NewServiceConfig(_ string) {}\n\nfunc (m *mockConn) ParseServiceConfig(_ string) *serviceconfig.ParseResult {\n\treturn nil\n}\n\nfunc TestDirectBuilder_Build(t *testing.T) {\n\tb := NewBuilder()\n\tr, err := b.Build(resolver.Target{}, &mockConn{}, resolver.BuildOptions{})\n\tif err != nil {\n\t\tt.Errorf(\"expect no error, got %v\", err)\n\t}\n\tr.ResolveNow(resolver.ResolveNowOptions{})\n\tr.Close()\n\n\t// need update state err\n\t_, err = b.Build(resolver.Target{}, &mockConn{needUpdateStateErr: true}, resolver.BuildOptions{})\n\tif err == nil {\n\t\tt.Errorf(\"expect needUpdateStateErr, got nil\")\n\t}\n}\n"
  },
  {
    "path": "transport/grpc/resolver/direct/resolver.go",
    "content": "package direct\n\nimport \"google.golang.org/grpc/resolver\"\n\ntype directResolver struct{}\n\nfunc newDirectResolver() resolver.Resolver {\n\treturn &directResolver{}\n}\n\nfunc (r *directResolver) Close() {\n}\n\nfunc (r *directResolver) ResolveNow(_ resolver.ResolveNowOptions) {\n}\n"
  },
  {
    "path": "transport/grpc/resolver/direct/resolver_test.go",
    "content": "package direct\n"
  },
  {
    "path": "transport/grpc/resolver/discovery/builder.go",
    "content": "package discovery\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/google/uuid\"\n\t\"google.golang.org/grpc/resolver\"\n\n\t\"github.com/go-kratos/kratos/v2/registry\"\n)\n\nconst name = \"discovery\"\n\nvar ErrWatcherCreateTimeout = errors.New(\"discovery create watcher overtime\")\n\n// Option is builder option.\ntype Option func(o *builder)\n\n// WithTimeout with timeout option.\nfunc WithTimeout(timeout time.Duration) Option {\n\treturn func(b *builder) {\n\t\tb.timeout = timeout\n\t}\n}\n\n// WithInsecure with isSecure option.\nfunc WithInsecure(insecure bool) Option {\n\treturn func(b *builder) {\n\t\tb.insecure = insecure\n\t}\n}\n\n// WithSubset with subset size.\nfunc WithSubset(size int) Option {\n\treturn func(b *builder) {\n\t\tb.subsetSize = size\n\t}\n}\n\n// Deprecated: please use PrintDebugLog\n// DisableDebugLog disables update instances log.\nfunc DisableDebugLog() Option {\n\treturn func(b *builder) {\n\t\tb.debugLog = false\n\t}\n}\n\n// PrintDebugLog print grpc resolver watch service log\nfunc PrintDebugLog(p bool) Option {\n\treturn func(b *builder) {\n\t\tb.debugLog = p\n\t}\n}\n\ntype builder struct {\n\tdiscoverer registry.Discovery\n\ttimeout    time.Duration\n\tinsecure   bool\n\tsubsetSize int\n\tdebugLog   bool\n}\n\n// NewBuilder creates a builder which is used to factory registry resolvers.\nfunc NewBuilder(d registry.Discovery, opts ...Option) resolver.Builder {\n\tb := &builder{\n\t\tdiscoverer: d,\n\t\ttimeout:    time.Second * 10,\n\t\tinsecure:   false,\n\t\tdebugLog:   true,\n\t\tsubsetSize: 25,\n\t}\n\tfor _, o := range opts {\n\t\to(b)\n\t}\n\treturn b\n}\n\nfunc (b *builder) Build(target resolver.Target, cc resolver.ClientConn, _ resolver.BuildOptions) (resolver.Resolver, error) {\n\twatchRes := &struct {\n\t\terr error\n\t\tw   registry.Watcher\n\t}{}\n\n\tdone := make(chan struct{}, 1)\n\tctx, cancel := context.WithCancel(context.Background())\n\tgo func() {\n\t\tw, err := b.discoverer.Watch(ctx, strings.TrimPrefix(target.URL.Path, \"/\"))\n\t\twatchRes.w = w\n\t\twatchRes.err = err\n\t\tclose(done)\n\t}()\n\n\tvar err error\n\tif b.timeout > 0 {\n\t\tselect {\n\t\tcase <-done:\n\t\t\terr = watchRes.err\n\t\tcase <-time.After(b.timeout):\n\t\t\terr = ErrWatcherCreateTimeout\n\t\t}\n\t} else {\n\t\t<-done\n\t\terr = watchRes.err\n\t}\n\tif err != nil {\n\t\tcancel()\n\t\treturn nil, err\n\t}\n\n\tr := &discoveryResolver{\n\t\tw:           watchRes.w,\n\t\tcc:          cc,\n\t\tctx:         ctx,\n\t\tcancel:      cancel,\n\t\tinsecure:    b.insecure,\n\t\tdebugLog:    b.debugLog,\n\t\tsubsetSize:  b.subsetSize,\n\t\tselectorKey: uuid.New().String(),\n\t}\n\tgo r.watch()\n\treturn r, nil\n}\n\n// Scheme return scheme of discovery\nfunc (*builder) Scheme() string {\n\treturn name\n}\n"
  },
  {
    "path": "transport/grpc/resolver/discovery/builder_test.go",
    "content": "package discovery\n\nimport (\n\t\"context\"\n\t\"net/url\"\n\t\"reflect\"\n\t\"testing\"\n\t\"time\"\n\n\t\"google.golang.org/grpc/resolver\"\n\t\"google.golang.org/grpc/serviceconfig\"\n\n\t\"github.com/go-kratos/kratos/v2/registry\"\n)\n\nfunc TestWithInsecure(t *testing.T) {\n\tb := &builder{}\n\tWithInsecure(true)(b)\n\tif !b.insecure {\n\t\tt.Errorf(\"expected insecure to be true\")\n\t}\n}\n\nfunc TestWithTimeout(t *testing.T) {\n\to := &builder{}\n\tv := time.Duration(123)\n\tWithTimeout(v)(o)\n\tif !reflect.DeepEqual(v, o.timeout) {\n\t\tt.Errorf(\"expected %v, got %v\", v, o.timeout)\n\t}\n}\n\nfunc TestDisableDebugLog(t *testing.T) {\n\to := &builder{}\n\tDisableDebugLog()(o)\n\tif o.debugLog {\n\t\tt.Errorf(\"expected debugLog true, got %v\", o.debugLog)\n\t}\n}\n\nfunc TestPrintDebugLog(t *testing.T) {\n\to := &builder{}\n\tPrintDebugLog(true)(o)\n\tif !o.debugLog {\n\t\tt.Errorf(\"expected PrintdebugLog true, got %v\", o.debugLog)\n\t}\n}\n\ntype mockDiscovery struct{}\n\nfunc (m *mockDiscovery) GetService(_ context.Context, _ string) ([]*registry.ServiceInstance, error) {\n\treturn nil, nil\n}\n\nfunc (m *mockDiscovery) Watch(_ context.Context, _ string) (registry.Watcher, error) {\n\ttime.Sleep(time.Microsecond * 500)\n\treturn &testWatch{}, nil\n}\n\nfunc TestBuilder_Scheme(t *testing.T) {\n\tb := NewBuilder(&mockDiscovery{})\n\tif !reflect.DeepEqual(\"discovery\", b.Scheme()) {\n\t\tt.Errorf(\"expected %v, got %v\", \"discovery\", b.Scheme())\n\t}\n}\n\ntype mockConn struct{}\n\nfunc (m *mockConn) UpdateState(resolver.State) error {\n\treturn nil\n}\n\nfunc (m *mockConn) ReportError(error) {}\n\nfunc (m *mockConn) NewAddress(_ []resolver.Address) {}\n\nfunc (m *mockConn) NewServiceConfig(_ string) {}\n\nfunc (m *mockConn) ParseServiceConfig(_ string) *serviceconfig.ParseResult {\n\treturn nil\n}\n\nfunc TestBuilder_Build(t *testing.T) {\n\tb := NewBuilder(&mockDiscovery{}, PrintDebugLog(false))\n\t_, err := b.Build(\n\t\tresolver.Target{\n\t\t\tURL: url.URL{\n\t\t\t\tScheme: resolver.GetDefaultScheme(),\n\t\t\t\tPath:   \"grpc://authority/endpoint\",\n\t\t\t},\n\t\t},\n\t\t&mockConn{},\n\t\tresolver.BuildOptions{},\n\t)\n\tif err != nil {\n\t\tt.Errorf(\"expected no error, got %v\", err)\n\t\treturn\n\t}\n\ttimeoutBuilder := NewBuilder(&mockDiscovery{}, WithTimeout(0))\n\t_, err = timeoutBuilder.Build(\n\t\tresolver.Target{\n\t\t\tURL: url.URL{\n\t\t\t\tScheme: resolver.GetDefaultScheme(),\n\t\t\t\tPath:   \"grpc://authority/endpoint\",\n\t\t\t},\n\t\t},\n\t\t&mockConn{},\n\t\tresolver.BuildOptions{},\n\t)\n\tif err != nil {\n\t\tt.Errorf(\"expected no error, got %v\", err)\n\t}\n}\n"
  },
  {
    "path": "transport/grpc/resolver/discovery/resolver.go",
    "content": "package discovery\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"time\"\n\n\t\"google.golang.org/grpc/attributes\"\n\t\"google.golang.org/grpc/resolver\"\n\n\t\"github.com/go-kratos/aegis/subset\"\n\t\"github.com/go-kratos/kratos/v2/internal/endpoint\"\n\t\"github.com/go-kratos/kratos/v2/log\"\n\t\"github.com/go-kratos/kratos/v2/registry\"\n)\n\ntype discoveryResolver struct {\n\tw  registry.Watcher\n\tcc resolver.ClientConn\n\n\tctx    context.Context\n\tcancel context.CancelFunc\n\n\tinsecure    bool\n\tdebugLog    bool\n\tselectorKey string\n\tsubsetSize  int\n}\n\nfunc (r *discoveryResolver) watch() {\n\tfor {\n\t\tselect {\n\t\tcase <-r.ctx.Done():\n\t\t\treturn\n\t\tdefault:\n\t\t}\n\t\tins, err := r.w.Next()\n\t\tif err != nil {\n\t\t\tif errors.Is(err, context.Canceled) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tlog.Errorf(\"[resolver] Failed to watch discovery endpoint: %v\", err)\n\t\t\ttime.Sleep(time.Second)\n\t\t\tcontinue\n\t\t}\n\t\tr.update(ins)\n\t}\n}\n\nfunc (r *discoveryResolver) update(ins []*registry.ServiceInstance) {\n\tvar (\n\t\tendpoints = make(map[string]struct{})\n\t\tfiltered  = make([]*registry.ServiceInstance, 0, len(ins))\n\t)\n\tfor _, in := range ins {\n\t\tept, err := endpoint.ParseEndpoint(in.Endpoints, endpoint.Scheme(\"grpc\", !r.insecure))\n\t\tif err != nil {\n\t\t\tlog.Errorf(\"[resolver] Failed to parse discovery endpoint: %v\", err)\n\t\t\tcontinue\n\t\t}\n\t\tif ept == \"\" {\n\t\t\tcontinue\n\t\t}\n\t\t// filter redundant endpoints\n\t\tif _, ok := endpoints[ept]; ok {\n\t\t\tcontinue\n\t\t}\n\t\tendpoints[ept] = struct{}{}\n\t\tfiltered = append(filtered, in)\n\t}\n\tif r.subsetSize != 0 {\n\t\tfiltered = subset.Subset(r.selectorKey, filtered, r.subsetSize)\n\t}\n\n\taddrs := make([]resolver.Address, 0, len(filtered))\n\tfor _, in := range filtered {\n\t\tept, _ := endpoint.ParseEndpoint(in.Endpoints, endpoint.Scheme(\"grpc\", !r.insecure))\n\t\taddr := resolver.Address{\n\t\t\tServerName: in.Name,\n\t\t\tAttributes: parseAttributes(in.Metadata).WithValue(\"rawServiceInstance\", in),\n\t\t\tAddr:       ept,\n\t\t}\n\t\taddrs = append(addrs, addr)\n\t}\n\tif len(addrs) == 0 {\n\t\tlog.Warnf(\"[resolver] Zero endpoint found,refused to write, instances: %v\", ins)\n\t\treturn\n\t}\n\terr := r.cc.UpdateState(resolver.State{Addresses: addrs})\n\tif err != nil {\n\t\tlog.Errorf(\"[resolver] failed to update state: %s\", err)\n\t}\n\tif r.debugLog {\n\t\tb, _ := json.Marshal(filtered)\n\t\tlog.Infof(\"[resolver] update instances: %s\", b)\n\t}\n}\n\nfunc (r *discoveryResolver) Close() {\n\tr.cancel()\n\terr := r.w.Stop()\n\tif err != nil {\n\t\tlog.Errorf(\"[resolver] failed to watch top: %s\", err)\n\t}\n}\n\nfunc (r *discoveryResolver) ResolveNow(_ resolver.ResolveNowOptions) {}\n\nfunc parseAttributes(md map[string]string) (a *attributes.Attributes) {\n\tfor k, v := range md {\n\t\ta = a.WithValue(k, v)\n\t}\n\treturn a\n}\n"
  },
  {
    "path": "transport/grpc/resolver/discovery/resolver_test.go",
    "content": "package discovery\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"reflect\"\n\t\"testing\"\n\t\"time\"\n\n\t\"google.golang.org/grpc/resolver\"\n\n\t\"github.com/go-kratos/kratos/v2/registry\"\n)\n\ntype testClientConn struct {\n\tresolver.ClientConn // For unimplemented functions\n\tte                  *testing.T\n}\n\nfunc (t *testClientConn) UpdateState(s resolver.State) error {\n\tt.te.Log(\"UpdateState\", s)\n\treturn nil\n}\n\ntype testWatch struct {\n\terr error\n\n\tcount uint\n}\n\nfunc (m *testWatch) Next() ([]*registry.ServiceInstance, error) {\n\ttime.Sleep(time.Millisecond * 200)\n\tif m.count > 1 {\n\t\treturn nil, nil\n\t}\n\tm.count++\n\tins := []*registry.ServiceInstance{\n\t\t{\n\t\t\tID:        \"mock_ID\",\n\t\t\tName:      \"mock_Name\",\n\t\t\tVersion:   \"mock_Version\",\n\t\t\tEndpoints: []string{\"grpc://127.0.0.1?isSecure=true\"},\n\t\t},\n\t\t{\n\t\t\tID:        \"mock_ID2\",\n\t\t\tName:      \"mock_Name2\",\n\t\t\tVersion:   \"mock_Version2\",\n\t\t\tEndpoints: []string{\"\"},\n\t\t},\n\t}\n\treturn ins, m.err\n}\n\n// Watch creates a watcher according to the service name.\nfunc (m *testWatch) Stop() error {\n\treturn m.err\n}\n\nfunc TestWatch(t *testing.T) {\n\tctx, cancel := context.WithCancel(context.Background())\n\n\tr := &discoveryResolver{\n\t\tw:        &testWatch{},\n\t\tcc:       &testClientConn{te: t},\n\t\tctx:      ctx,\n\t\tcancel:   cancel,\n\t\tinsecure: false,\n\t}\n\tr.ResolveNow(resolver.ResolveNowOptions{})\n\tgo func() {\n\t\ttime.Sleep(time.Second * 2)\n\t\tr.Close()\n\t}()\n\tr.watch()\n\tt.Log(\"watch goroutine exited after 2 second\")\n}\n\nfunc TestWatchError(t *testing.T) {\n\tctx, cancel := context.WithCancel(context.Background())\n\n\tr := &discoveryResolver{\n\t\tw:      &testWatch{err: errors.New(\"bad\")},\n\t\tcc:     &testClientConn{te: t},\n\t\tctx:    ctx,\n\t\tcancel: cancel,\n\t}\n\tgo func() {\n\t\ttime.Sleep(time.Second * 2)\n\t\tr.Close()\n\t}()\n\tr.watch()\n\tt.Log(\"watch goroutine exited after 2 second\")\n}\n\nfunc TestWatchContextCancel(t *testing.T) {\n\tctx, cancel := context.WithCancel(context.Background())\n\n\tr := &discoveryResolver{\n\t\tw:      &testWatch{err: context.Canceled},\n\t\tcc:     &testClientConn{te: t},\n\t\tctx:    ctx,\n\t\tcancel: cancel,\n\t}\n\tgo func() {\n\t\ttime.Sleep(time.Second * 2)\n\t\tr.Close()\n\t}()\n\tr.watch()\n\tt.Log(\"watch goroutine exited after 2 second\")\n}\n\nfunc TestParseAttributes(t *testing.T) {\n\ta := parseAttributes(map[string]string{\n\t\t\"a\": \"b\",\n\t\t\"c\": \"d\",\n\t})\n\tif !reflect.DeepEqual(\"b\", a.Value(\"a\").(string)) {\n\t\tt.Errorf(\"expect b, got %v\", a.Value(\"a\"))\n\t}\n\tx := a.WithValue(\"qq\", \"ww\")\n\tif !reflect.DeepEqual(\"ww\", x.Value(\"qq\").(string)) {\n\t\tt.Errorf(\"expect ww, got %v\", x.Value(\"qq\"))\n\t}\n\tif x.Value(\"notfound\") != nil {\n\t\tt.Errorf(\"expect nil, got %v\", x.Value(\"notfound\"))\n\t}\n}\n"
  },
  {
    "path": "transport/grpc/server.go",
    "content": "package grpc\n\nimport (\n\t\"context\"\n\t\"crypto/tls\"\n\t\"net\"\n\t\"net/url\"\n\t\"time\"\n\n\t\"google.golang.org/grpc\"\n\t\"google.golang.org/grpc/admin\"\n\t\"google.golang.org/grpc/credentials\"\n\t\"google.golang.org/grpc/health\"\n\t\"google.golang.org/grpc/health/grpc_health_v1\"\n\t\"google.golang.org/grpc/reflection\"\n\n\tapimd \"github.com/go-kratos/kratos/v2/api/metadata\"\n\t\"github.com/go-kratos/kratos/v2/internal/endpoint\"\n\t\"github.com/go-kratos/kratos/v2/internal/host\"\n\t\"github.com/go-kratos/kratos/v2/internal/matcher\"\n\t\"github.com/go-kratos/kratos/v2/log\"\n\t\"github.com/go-kratos/kratos/v2/middleware\"\n\t\"github.com/go-kratos/kratos/v2/transport\"\n)\n\nvar (\n\t_ transport.Server     = (*Server)(nil)\n\t_ transport.Endpointer = (*Server)(nil)\n)\n\n// ServerOption is gRPC server option.\ntype ServerOption func(o *Server)\n\n// Network with server network.\nfunc Network(network string) ServerOption {\n\treturn func(s *Server) {\n\t\ts.network = network\n\t}\n}\n\n// Address with server address.\nfunc Address(addr string) ServerOption {\n\treturn func(s *Server) {\n\t\ts.address = addr\n\t}\n}\n\n// Endpoint with server address.\nfunc Endpoint(endpoint *url.URL) ServerOption {\n\treturn func(s *Server) {\n\t\ts.endpoint = endpoint\n\t}\n}\n\n// Timeout with server timeout.\nfunc Timeout(timeout time.Duration) ServerOption {\n\treturn func(s *Server) {\n\t\ts.timeout = timeout\n\t}\n}\n\n// Logger with server logger.\n// Deprecated: use global logger instead.\nfunc Logger(log.Logger) ServerOption {\n\treturn func(*Server) {}\n}\n\n// Middleware with server middleware.\nfunc Middleware(m ...middleware.Middleware) ServerOption {\n\treturn func(s *Server) {\n\t\ts.middleware.Use(m...)\n\t}\n}\n\nfunc StreamMiddleware(m ...middleware.Middleware) ServerOption {\n\treturn func(s *Server) {\n\t\ts.streamMiddleware.Use(m...)\n\t}\n}\n\n// CustomHealth Checks server.\nfunc CustomHealth() ServerOption {\n\treturn func(s *Server) {\n\t\ts.customHealth = true\n\t}\n}\n\n// TLSConfig with TLS config.\nfunc TLSConfig(c *tls.Config) ServerOption {\n\treturn func(s *Server) {\n\t\ts.tlsConf = c\n\t}\n}\n\n// Listener with server lis\nfunc Listener(lis net.Listener) ServerOption {\n\treturn func(s *Server) {\n\t\ts.lis = lis\n\t}\n}\n\n// UnaryInterceptor returns a ServerOption that sets the UnaryServerInterceptor for the server.\nfunc UnaryInterceptor(in ...grpc.UnaryServerInterceptor) ServerOption {\n\treturn func(s *Server) {\n\t\ts.unaryInts = in\n\t}\n}\n\n// StreamInterceptor returns a ServerOption that sets the StreamServerInterceptor for the server.\nfunc StreamInterceptor(in ...grpc.StreamServerInterceptor) ServerOption {\n\treturn func(s *Server) {\n\t\ts.streamInts = in\n\t}\n}\n\n// DisableReflection disable grpc reflection.\nfunc DisableReflection() ServerOption {\n\treturn func(s *Server) {\n\t\ts.disableReflection = true\n\t}\n}\n\n// Options with grpc options.\nfunc Options(opts ...grpc.ServerOption) ServerOption {\n\treturn func(s *Server) {\n\t\ts.grpcOpts = opts\n\t}\n}\n\n// Server is a gRPC server wrapper.\ntype Server struct {\n\t*grpc.Server\n\tbaseCtx           context.Context\n\ttlsConf           *tls.Config\n\tlis               net.Listener\n\terr               error\n\tnetwork           string\n\taddress           string\n\tendpoint          *url.URL\n\ttimeout           time.Duration\n\tmiddleware        matcher.Matcher\n\tstreamMiddleware  matcher.Matcher\n\tunaryInts         []grpc.UnaryServerInterceptor\n\tstreamInts        []grpc.StreamServerInterceptor\n\tgrpcOpts          []grpc.ServerOption\n\thealth            *health.Server\n\tcustomHealth      bool\n\tmetadata          *apimd.Server\n\tadminClean        func()\n\tdisableReflection bool\n}\n\n// NewServer creates a gRPC server by options.\nfunc NewServer(opts ...ServerOption) *Server {\n\tsrv := &Server{\n\t\tbaseCtx:          context.Background(),\n\t\tnetwork:          \"tcp\",\n\t\taddress:          \":0\",\n\t\ttimeout:          1 * time.Second,\n\t\thealth:           health.NewServer(),\n\t\tmiddleware:       matcher.New(),\n\t\tstreamMiddleware: matcher.New(),\n\t}\n\tfor _, o := range opts {\n\t\to(srv)\n\t}\n\tunaryInts := []grpc.UnaryServerInterceptor{\n\t\tsrv.unaryServerInterceptor(),\n\t}\n\tstreamInts := []grpc.StreamServerInterceptor{\n\t\tsrv.streamServerInterceptor(),\n\t}\n\tif len(srv.unaryInts) > 0 {\n\t\tunaryInts = append(unaryInts, srv.unaryInts...)\n\t}\n\tif len(srv.streamInts) > 0 {\n\t\tstreamInts = append(streamInts, srv.streamInts...)\n\t}\n\tgrpcOpts := []grpc.ServerOption{\n\t\tgrpc.ChainUnaryInterceptor(unaryInts...),\n\t\tgrpc.ChainStreamInterceptor(streamInts...),\n\t}\n\tif srv.tlsConf != nil {\n\t\tgrpcOpts = append(grpcOpts, grpc.Creds(credentials.NewTLS(srv.tlsConf)))\n\t}\n\tif len(srv.grpcOpts) > 0 {\n\t\tgrpcOpts = append(grpcOpts, srv.grpcOpts...)\n\t}\n\tsrv.Server = grpc.NewServer(grpcOpts...)\n\tsrv.metadata = apimd.NewServer(srv.Server)\n\t// internal register\n\tif !srv.customHealth {\n\t\tgrpc_health_v1.RegisterHealthServer(srv.Server, srv.health)\n\t}\n\tapimd.RegisterMetadataServer(srv.Server, srv.metadata)\n\t// reflection register\n\tif !srv.disableReflection {\n\t\treflection.Register(srv.Server)\n\t}\n\t// admin register\n\tsrv.adminClean, _ = admin.Register(srv.Server)\n\treturn srv\n}\n\n// Use uses a service middleware with selector.\n// selector:\n//   - '/*'\n//   - '/helloworld.v1.Greeter/*'\n//   - '/helloworld.v1.Greeter/SayHello'\nfunc (s *Server) Use(selector string, m ...middleware.Middleware) {\n\ts.middleware.Add(selector, m...)\n}\n\n// Endpoint return a real address to registry endpoint.\n// examples:\n//\n//\tgrpc://127.0.0.1:9000?isSecure=false\nfunc (s *Server) Endpoint() (*url.URL, error) {\n\tif err := s.listenAndEndpoint(); err != nil {\n\t\treturn nil, s.err\n\t}\n\treturn s.endpoint, nil\n}\n\n// Start start the gRPC server.\nfunc (s *Server) Start(ctx context.Context) error {\n\tif err := s.listenAndEndpoint(); err != nil {\n\t\treturn s.err\n\t}\n\ts.baseCtx = ctx\n\tlog.Infof(\"[gRPC] server listening on: %s\", s.lis.Addr().String())\n\ts.health.Resume()\n\treturn s.Serve(s.lis)\n}\n\n// Stop stop the gRPC server.\nfunc (s *Server) Stop(ctx context.Context) error {\n\tif s.adminClean != nil {\n\t\ts.adminClean()\n\t}\n\ts.health.Shutdown()\n\n\tdone := make(chan struct{})\n\tgo func() {\n\t\tdefer close(done)\n\t\tlog.Info(\"[gRPC] server stopping\")\n\t\ts.GracefulStop()\n\t}()\n\n\tselect {\n\tcase <-done:\n\tcase <-ctx.Done():\n\t\tlog.Warn(\"[gRPC] server couldn't stop gracefully in time, doing force stop\")\n\t\ts.Server.Stop()\n\t}\n\treturn nil\n}\n\nfunc (s *Server) listenAndEndpoint() error {\n\tif s.lis == nil {\n\t\tlis, err := net.Listen(s.network, s.address)\n\t\tif err != nil {\n\t\t\ts.err = err\n\t\t\treturn err\n\t\t}\n\t\ts.lis = lis\n\t}\n\tif s.endpoint == nil {\n\t\taddr, err := host.Extract(s.address, s.lis)\n\t\tif err != nil {\n\t\t\ts.err = err\n\t\t\treturn err\n\t\t}\n\t\ts.endpoint = endpoint.NewEndpoint(endpoint.Scheme(\"grpc\", s.tlsConf != nil), addr)\n\t}\n\treturn s.err\n}\n"
  },
  {
    "path": "transport/grpc/server_test.go",
    "content": "package grpc\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"crypto/tls\"\n\t\"fmt\"\n\t\"net\"\n\t\"net/url\"\n\t\"reflect\"\n\t\"strings\"\n\t\"sync\"\n\t\"testing\"\n\t\"time\"\n\n\t\"google.golang.org/grpc\"\n\t\"google.golang.org/grpc/metadata\"\n\n\t\"github.com/go-kratos/kratos/v2/errors\"\n\t\"github.com/go-kratos/kratos/v2/internal/matcher\"\n\tpb \"github.com/go-kratos/kratos/v2/internal/testdata/helloworld\"\n\t\"github.com/go-kratos/kratos/v2/log\"\n\t\"github.com/go-kratos/kratos/v2/middleware\"\n\t\"github.com/go-kratos/kratos/v2/transport\"\n)\n\n// server is used to implement helloworld.GreeterServer.\ntype server struct {\n\tpb.UnimplementedGreeterServer\n}\n\nfunc (s *server) SayHelloStream(streamServer pb.Greeter_SayHelloStreamServer) error {\n\ttctx, ok := transport.FromServerContext(streamServer.Context())\n\tif ok {\n\t\ttctx.ReplyHeader().Set(\"123\", \"123\")\n\t}\n\tvar cnt uint\n\tfor {\n\t\tin, err := streamServer.Recv()\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif in.Name == \"error\" {\n\t\t\treturn errors.BadRequest(\"custom_error\", fmt.Sprintf(\"invalid argument %s\", in.Name))\n\t\t}\n\t\tif in.Name == \"panic\" {\n\t\t\tpanic(\"server panic\")\n\t\t}\n\t\terr = streamServer.Send(&pb.HelloReply{\n\t\t\tMessage: fmt.Sprintf(\"hello %s\", in.Name),\n\t\t})\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tcnt++\n\t\tif cnt > 1 {\n\t\t\treturn nil\n\t\t}\n\t}\n}\n\n// SayHello implements helloworld.GreeterServer\nfunc (s *server) SayHello(_ context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) {\n\tif in.Name == \"error\" {\n\t\treturn nil, errors.BadRequest(\"custom_error\", fmt.Sprintf(\"invalid argument %s\", in.Name))\n\t}\n\tif in.Name == \"panic\" {\n\t\tpanic(\"server panic\")\n\t}\n\treturn &pb.HelloReply{Message: fmt.Sprintf(\"Hello %+v\", in.Name)}, nil\n}\n\ntype testKey struct{}\n\nfunc TestServer(t *testing.T) {\n\tctx := context.Background()\n\tctx = context.WithValue(ctx, testKey{}, \"test\")\n\tsrv := NewServer(\n\t\tMiddleware(\n\t\t\tfunc(handler middleware.Handler) middleware.Handler {\n\t\t\t\treturn func(ctx context.Context, req any) (reply any, err error) {\n\t\t\t\t\tif tr, ok := transport.FromServerContext(ctx); ok {\n\t\t\t\t\t\tif tr.ReplyHeader() != nil {\n\t\t\t\t\t\t\ttr.ReplyHeader().Set(\"req_id\", \"3344\")\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\treturn handler(ctx, req)\n\t\t\t\t}\n\t\t\t}),\n\t\tUnaryInterceptor(func(ctx context.Context, req any, _ *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp any, err error) {\n\t\t\treturn handler(ctx, req)\n\t\t}),\n\t\tOptions(grpc.InitialConnWindowSize(0)),\n\t)\n\tpb.RegisterGreeterServer(srv, &server{})\n\n\tif e, err := srv.Endpoint(); err != nil || e == nil || strings.HasSuffix(e.Host, \":0\") {\n\t\tt.Fatal(e, err)\n\t}\n\n\tgo func() {\n\t\t// start server\n\t\tif err := srv.Start(ctx); err != nil {\n\t\t\tpanic(err)\n\t\t}\n\t}()\n\ttime.Sleep(time.Second)\n\ttestClient(t, srv)\n\t_ = srv.Stop(ctx)\n}\n\nfunc testClient(t *testing.T, srv *Server) {\n\tu, err := srv.Endpoint()\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\t// new a gRPC client\n\tconn, err := DialInsecure(context.Background(),\n\t\tWithEndpoint(u.Host),\n\t\tWithOptions(grpc.WithBlock()),\n\t\tWithUnaryInterceptor(\n\t\t\tfunc(ctx context.Context, method string, req, reply any, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error {\n\t\t\t\treturn invoker(ctx, method, req, reply, cc, opts...)\n\t\t\t}),\n\t\tWithMiddleware(func(handler middleware.Handler) middleware.Handler {\n\t\t\treturn func(ctx context.Context, req any) (reply any, err error) {\n\t\t\t\tif tr, ok := transport.FromClientContext(ctx); ok {\n\t\t\t\t\theader := tr.RequestHeader()\n\t\t\t\t\theader.Set(\"x-md-trace\", \"2233\")\n\t\t\t\t}\n\t\t\t\treturn handler(ctx, req)\n\t\t\t}\n\t\t}),\n\t)\n\tdefer func() {\n\t\t_ = conn.Close()\n\t}()\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tclient := pb.NewGreeterClient(conn)\n\treply, err := client.SayHello(context.Background(), &pb.HelloRequest{Name: \"kratos\"})\n\tt.Log(err)\n\tif err != nil {\n\t\tt.Errorf(\"failed to call: %v\", err)\n\t}\n\tif !reflect.DeepEqual(reply.Message, \"Hello kratos\") {\n\t\tt.Errorf(\"expect %s, got %s\", \"Hello kratos\", reply.Message)\n\t}\n\n\tstreamCli, err := client.SayHelloStream(context.Background())\n\tif err != nil {\n\t\tt.Error(err)\n\t\treturn\n\t}\n\tdefer func() {\n\t\t_ = streamCli.CloseSend()\n\t}()\n\terr = streamCli.Send(&pb.HelloRequest{Name: \"cc\"})\n\tif err != nil {\n\t\tt.Error(err)\n\t\treturn\n\t}\n\treply, err = streamCli.Recv()\n\tif err != nil {\n\t\tt.Error(err)\n\t\treturn\n\t}\n\tif !reflect.DeepEqual(reply.Message, \"hello cc\") {\n\t\tt.Errorf(\"expect %s, got %s\", \"hello cc\", reply.Message)\n\t}\n}\n\nfunc TestNetwork(t *testing.T) {\n\to := &Server{}\n\tv := \"abc\"\n\tNetwork(v)(o)\n\tif !reflect.DeepEqual(v, o.network) {\n\t\tt.Errorf(\"expect %s, got %s\", v, o.network)\n\t}\n}\n\nfunc TestAddress(t *testing.T) {\n\tv := \"abc\"\n\to := NewServer(Address(v))\n\tif !reflect.DeepEqual(v, o.address) {\n\t\tt.Errorf(\"expect %s, got %s\", v, o.address)\n\t}\n\tu, err := o.Endpoint()\n\tif err == nil {\n\t\tt.Errorf(\"expect %s, got %s\", v, err)\n\t}\n\tif u != nil {\n\t\tt.Errorf(\"expect %s, got %s\", v, u)\n\t}\n}\n\nfunc TestTimeout(t *testing.T) {\n\to := &Server{}\n\tv := time.Duration(123)\n\tTimeout(v)(o)\n\tif !reflect.DeepEqual(v, o.timeout) {\n\t\tt.Errorf(\"expect %s, got %s\", v, o.timeout)\n\t}\n}\n\nfunc TestTLSConfig(t *testing.T) {\n\to := &Server{}\n\tv := &tls.Config{}\n\tTLSConfig(v)(o)\n\tif !reflect.DeepEqual(v, o.tlsConf) {\n\t\tt.Errorf(\"expect %v, got %v\", v, o.tlsConf)\n\t}\n}\n\nfunc TestUnaryInterceptor(t *testing.T) {\n\to := &Server{}\n\tv := []grpc.UnaryServerInterceptor{\n\t\tfunc(context.Context, any, *grpc.UnaryServerInfo, grpc.UnaryHandler) (resp any, err error) {\n\t\t\treturn nil, nil\n\t\t},\n\t\tfunc(context.Context, any, *grpc.UnaryServerInfo, grpc.UnaryHandler) (resp any, err error) {\n\t\t\treturn nil, nil\n\t\t},\n\t}\n\tUnaryInterceptor(v...)(o)\n\tif !reflect.DeepEqual(v, o.unaryInts) {\n\t\tt.Errorf(\"expect %v, got %v\", v, o.unaryInts)\n\t}\n}\n\nfunc TestStreamInterceptor(t *testing.T) {\n\to := &Server{}\n\tv := []grpc.StreamServerInterceptor{\n\t\tfunc(any, grpc.ServerStream, *grpc.StreamServerInfo, grpc.StreamHandler) error {\n\t\t\treturn nil\n\t\t},\n\t\tfunc(any, grpc.ServerStream, *grpc.StreamServerInfo, grpc.StreamHandler) error {\n\t\t\treturn nil\n\t\t},\n\t}\n\tStreamInterceptor(v...)(o)\n\tif !reflect.DeepEqual(v, o.streamInts) {\n\t\tt.Errorf(\"expect %v, got %v\", v, o.streamInts)\n\t}\n}\n\nfunc TestOptions(t *testing.T) {\n\to := &Server{}\n\tv := []grpc.ServerOption{\n\t\tgrpc.EmptyServerOption{},\n\t}\n\tOptions(v...)(o)\n\tif !reflect.DeepEqual(v, o.grpcOpts) {\n\t\tt.Errorf(\"expect %v, got %v\", v, o.grpcOpts)\n\t}\n}\n\ntype testResp struct {\n\tData string\n}\n\nfunc TestServer_unaryServerInterceptor(t *testing.T) {\n\tu, err := url.Parse(\"grpc://hello/world\")\n\tif err != nil {\n\t\tt.Errorf(\"expect %v, got %v\", nil, err)\n\t}\n\tsrv := &Server{\n\t\tbaseCtx:    context.Background(),\n\t\tendpoint:   u,\n\t\ttimeout:    time.Duration(10),\n\t\tmiddleware: matcher.New(),\n\t}\n\tsrv.middleware.Use(EmptyMiddleware())\n\treq := &struct{}{}\n\trv, err := srv.unaryServerInterceptor()(context.TODO(), req, &grpc.UnaryServerInfo{}, func(context.Context, any) (any, error) {\n\t\treturn &testResp{Data: \"hi\"}, nil\n\t})\n\tif err != nil {\n\t\tt.Errorf(\"expect %v, got %v\", nil, err)\n\t}\n\tif !reflect.DeepEqual(\"hi\", rv.(*testResp).Data) {\n\t\tt.Errorf(\"expect %s, got %s\", \"hi\", rv.(*testResp).Data)\n\t}\n}\n\ntype mockServerStream struct {\n\tctx      context.Context\n\tsentMsg  any\n\trecvMsg  any\n\tmetadata metadata.MD\n\tgrpc.ServerStream\n}\n\nfunc (m *mockServerStream) SetHeader(md metadata.MD) error {\n\tm.metadata = md\n\treturn nil\n}\n\nfunc (m *mockServerStream) SendHeader(md metadata.MD) error {\n\tm.metadata = md\n\treturn nil\n}\n\nfunc (m *mockServerStream) SetTrailer(md metadata.MD) {\n\tm.metadata = md\n}\n\nfunc (m *mockServerStream) Context() context.Context {\n\treturn m.ctx\n}\n\nfunc (m *mockServerStream) SendMsg(msg any) error {\n\tm.sentMsg = msg\n\treturn nil\n}\n\nfunc (m *mockServerStream) RecvMsg(msg any) error {\n\tm.recvMsg = msg\n\treturn nil\n}\n\nfunc TestServer_streamServerInterceptor(t *testing.T) {\n\tu, err := url.Parse(\"grpc://hello/world\")\n\tif err != nil {\n\t\tt.Errorf(\"expect %v, got %v\", nil, err)\n\t}\n\tsrv := &Server{\n\t\tbaseCtx:          context.Background(),\n\t\tendpoint:         u,\n\t\ttimeout:          time.Duration(10),\n\t\tmiddleware:       matcher.New(),\n\t\tstreamMiddleware: matcher.New(),\n\t}\n\n\tsrv.streamMiddleware.Use(EmptyMiddleware())\n\n\tmockStream := &mockServerStream{\n\t\tctx: srv.baseCtx,\n\t}\n\n\thandler := func(_ any, stream grpc.ServerStream) error {\n\t\tresp := &testResp{Data: \"stream hi\"}\n\t\treturn stream.SendMsg(resp)\n\t}\n\n\tinfo := &grpc.StreamServerInfo{\n\t\tFullMethod: \"/grpc.reflection.v1.ServerReflection/ServerReflectionInfo\",\n\t}\n\n\terr = srv.streamServerInterceptor()(nil, mockStream, info, handler)\n\tif err != nil {\n\t\tt.Errorf(\"expect %v, got %v\", nil, err)\n\t}\n\n\t// Check response\n\tresp := mockStream.sentMsg.(*testResp)\n\tif !reflect.DeepEqual(\"stream hi\", resp.Data) {\n\t\tt.Errorf(\"expect %s, got %s\", \"stream hi\", resp.Data)\n\t}\n}\n\nfunc TestListener(t *testing.T) {\n\tlis, err := net.Listen(\"tcp\", \":0\")\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\ts := &Server{}\n\tListener(lis)(s)\n\tif !reflect.DeepEqual(lis, s.lis) {\n\t\tt.Errorf(\"expect %v, got %v\", lis, s.lis)\n\t}\n\tif e, err := s.Endpoint(); err != nil || e == nil {\n\t\tt.Errorf(\"expect not empty\")\n\t}\n}\n\nfunc TestStop(t *testing.T) {\n\ttimeoutCtx, cancel := context.WithTimeout(context.Background(), 10*time.Millisecond)\n\tdefer cancel()\n\n\ttests := []struct {\n\t\tname          string\n\t\tctx           context.Context\n\t\tcancel        context.CancelFunc\n\t\twantForceStop bool\n\t}{\n\t\t{\n\t\t\tname:          \"normal\",\n\t\t\tctx:           context.Background(),\n\t\t\tcancel:        func() {},\n\t\t\twantForceStop: false,\n\t\t},\n\t\t{\n\t\t\tname:          \"timeout\",\n\t\t\tctx:           timeoutCtx,\n\t\t\tcancel:        cancel,\n\t\t\twantForceStop: true,\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tl, err := net.Listen(\"tcp\", \":0\")\n\t\t\tif err != nil {\n\t\t\t\tt.Fatal(err)\n\t\t\t}\n\t\t\tdefer l.Close()\n\n\t\t\told := log.GetLogger()\n\t\t\tdefer log.SetLogger(old)\n\n\t\t\t// Create a logger to capture logs\n\t\t\tvar logs safeBytesBuffer\n\t\t\tlog.SetLogger(log.NewStdLogger(&logs))\n\n\t\t\ts := NewServer(Listener(l))\n\t\t\tpb.RegisterGreeterServer(s, &server{})\n\n\t\t\tgo func() {\n\t\t\t\terr := s.Start(context.Background()) //nolint\n\t\t\t\tif err != nil {\n\t\t\t\t\tlog.Fatal(err)\n\t\t\t\t}\n\t\t\t}()\n\n\t\t\ttime.Sleep(100 * time.Millisecond)\n\n\t\t\tconn, err := DialInsecure(\n\t\t\t\tcontext.Background(),\n\t\t\t\tWithEndpoint(l.Addr().String()),\n\t\t\t\tWithOptions(grpc.WithBlock()),\n\t\t\t)\n\t\t\tif err != nil {\n\t\t\t\tt.Fatal(err)\n\t\t\t}\n\t\t\tdefer conn.Close()\n\n\t\t\tgo func() {\n\t\t\t\tclient := pb.NewGreeterClient(conn)\n\t\t\t\tif tt.wantForceStop {\n\t\t\t\t\t// Simulate a long-running request\n\t\t\t\t\ts, err := client.SayHelloStream(context.Background()) //nolint\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\tlog.Fatal(err)\n\t\t\t\t\t}\n\t\t\t\t\t// Keep the stream open\n\t\t\t\t\tfor {\n\t\t\t\t\t\t// Intentionally do not send messages, only receive messages\n\t\t\t\t\t\t_, err := s.Recv()\n\t\t\t\t\t\tif err != nil {\n\t\t\t\t\t\t\tbreak\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\t_, err := client.SayHello(context.Background(), &pb.HelloRequest{Name: \"test\"}) //nolint\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\tlog.Error(err)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}()\n\n\t\t\ttime.Sleep(100 * time.Millisecond)\n\n\t\t\terr = s.Stop(tt.ctx)\n\t\t\tif err != nil {\n\t\t\t\tt.Errorf(\"Expected no error, got %v\", err)\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\t// Check if the stop was forced or graceful\n\t\t\tif tt.wantForceStop {\n\t\t\t\tif !strings.Contains(logs.String(), \"force stop\") {\n\t\t\t\t\tt.Errorf(\"Expected force stop\\n%s\", logs.String())\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tif strings.Contains(logs.String(), \"force stop\") {\n\t\t\t\t\tt.Errorf(\"Expected graceful stop\\n%s\", logs.String())\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t}\n}\n\ntype safeBytesBuffer struct {\n\tmu  sync.Mutex\n\tbuf bytes.Buffer\n}\n\nfunc (b *safeBytesBuffer) Write(p []byte) (n int, err error) {\n\tb.mu.Lock()\n\tdefer b.mu.Unlock()\n\treturn b.buf.Write(p)\n}\n\nfunc (b *safeBytesBuffer) String() string {\n\tb.mu.Lock()\n\tdefer b.mu.Unlock()\n\treturn b.buf.String()\n}\n"
  },
  {
    "path": "transport/grpc/transport.go",
    "content": "package grpc\n\nimport (\n\t\"google.golang.org/grpc/metadata\"\n\n\t\"github.com/go-kratos/kratos/v2/selector\"\n\t\"github.com/go-kratos/kratos/v2/transport\"\n)\n\nvar _ transport.Transporter = (*Transport)(nil)\n\n// Transport is a gRPC transport.\ntype Transport struct {\n\tendpoint    string\n\toperation   string\n\treqHeader   headerCarrier\n\treplyHeader headerCarrier\n\tnodeFilters []selector.NodeFilter\n}\n\n// Kind returns the transport kind.\nfunc (tr *Transport) Kind() transport.Kind {\n\treturn transport.KindGRPC\n}\n\n// Endpoint returns the transport endpoint.\nfunc (tr *Transport) Endpoint() string {\n\treturn tr.endpoint\n}\n\n// Operation returns the transport operation.\nfunc (tr *Transport) Operation() string {\n\treturn tr.operation\n}\n\n// RequestHeader returns the request header.\nfunc (tr *Transport) RequestHeader() transport.Header {\n\treturn tr.reqHeader\n}\n\n// ReplyHeader returns the reply header.\nfunc (tr *Transport) ReplyHeader() transport.Header {\n\treturn tr.replyHeader\n}\n\n// NodeFilters returns the client select filters.\nfunc (tr *Transport) NodeFilters() []selector.NodeFilter {\n\treturn tr.nodeFilters\n}\n\ntype headerCarrier metadata.MD\n\n// Get returns the value associated with the passed key.\nfunc (mc headerCarrier) Get(key string) string {\n\tvals := metadata.MD(mc).Get(key)\n\tif len(vals) > 0 {\n\t\treturn vals[0]\n\t}\n\treturn \"\"\n}\n\n// Set stores the key-value pair.\nfunc (mc headerCarrier) Set(key string, value string) {\n\tmetadata.MD(mc).Set(key, value)\n}\n\n// Add append value to key-values pair.\nfunc (mc headerCarrier) Add(key string, value string) {\n\tmetadata.MD(mc).Append(key, value)\n}\n\n// Keys lists the keys stored in this carrier.\nfunc (mc headerCarrier) Keys() []string {\n\tkeys := make([]string, 0, len(mc))\n\tfor k := range metadata.MD(mc) {\n\t\tkeys = append(keys, k)\n\t}\n\treturn keys\n}\n\n// Values returns a slice of values associated with the passed key.\nfunc (mc headerCarrier) Values(key string) []string {\n\treturn metadata.MD(mc).Get(key)\n}\n"
  },
  {
    "path": "transport/grpc/transport_test.go",
    "content": "package grpc\n\nimport (\n\t\"reflect\"\n\t\"sort\"\n\t\"testing\"\n\n\t\"github.com/go-kratos/kratos/v2/transport\"\n)\n\nfunc TestTransport_Kind(t *testing.T) {\n\to := &Transport{}\n\tif !reflect.DeepEqual(transport.KindGRPC, o.Kind()) {\n\t\tt.Errorf(\"expect %v, got %v\", transport.KindGRPC, o.Kind())\n\t}\n}\n\nfunc TestTransport_Endpoint(t *testing.T) {\n\tv := \"hello\"\n\to := &Transport{endpoint: v}\n\tif !reflect.DeepEqual(v, o.Endpoint()) {\n\t\tt.Errorf(\"expect %v, got %v\", v, o.Endpoint())\n\t}\n}\n\nfunc TestTransport_Operation(t *testing.T) {\n\tv := \"hello\"\n\to := &Transport{operation: v}\n\tif !reflect.DeepEqual(v, o.Operation()) {\n\t\tt.Errorf(\"expect %v, got %v\", v, o.Operation())\n\t}\n}\n\nfunc TestTransport_RequestHeader(t *testing.T) {\n\tv := headerCarrier{}\n\tv.Set(\"a\", \"1\")\n\to := &Transport{reqHeader: v}\n\tif !reflect.DeepEqual(\"1\", o.RequestHeader().Get(\"a\")) {\n\t\tt.Errorf(\"expect %v, got %v\", \"1\", o.RequestHeader().Get(\"a\"))\n\t}\n\tif !reflect.DeepEqual(\"\", o.RequestHeader().Get(\"notfound\")) {\n\t\tt.Errorf(\"expect %v, got %v\", \"\", o.RequestHeader().Get(\"notfound\"))\n\t}\n}\n\nfunc TestTransport_ReplyHeader(t *testing.T) {\n\tv := headerCarrier{}\n\tv.Set(\"a\", \"1\")\n\to := &Transport{replyHeader: v}\n\tif !reflect.DeepEqual(\"1\", o.ReplyHeader().Get(\"a\")) {\n\t\tt.Errorf(\"expect %v, got %v\", \"1\", o.ReplyHeader().Get(\"a\"))\n\t}\n}\n\nfunc TestHeaderCarrier_Keys(t *testing.T) {\n\tv := headerCarrier{}\n\tv.Set(\"abb\", \"1\")\n\tv.Set(\"bcc\", \"2\")\n\twant := []string{\"abb\", \"bcc\"}\n\tkeys := v.Keys()\n\tsort.Slice(want, func(i, j int) bool {\n\t\treturn want[i] < want[j]\n\t})\n\tsort.Slice(keys, func(i, j int) bool {\n\t\treturn keys[i] < keys[j]\n\t})\n\tif !reflect.DeepEqual(want, keys) {\n\t\tt.Errorf(\"expect %v, got %v\", want, keys)\n\t}\n}\n"
  },
  {
    "path": "transport/http/binding/bind.go",
    "content": "package binding\n\nimport (\n\t\"net/http\"\n\t\"net/url\"\n\n\t\"github.com/go-kratos/kratos/v2/encoding\"\n\t\"github.com/go-kratos/kratos/v2/encoding/form\"\n\t\"github.com/go-kratos/kratos/v2/errors\"\n)\n\n// BindQuery bind vars parameters to target.\nfunc BindQuery(vars url.Values, target any) error {\n\tif err := encoding.GetCodec(form.Name).Unmarshal([]byte(vars.Encode()), target); err != nil {\n\t\treturn errors.BadRequest(\"CODEC\", err.Error())\n\t}\n\treturn nil\n}\n\n// BindForm bind form parameters to target.\nfunc BindForm(req *http.Request, target any) error {\n\tif err := req.ParseForm(); err != nil {\n\t\treturn err\n\t}\n\tif err := encoding.GetCodec(form.Name).Unmarshal([]byte(req.Form.Encode()), target); err != nil {\n\t\treturn errors.BadRequest(\"CODEC\", err.Error())\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "transport/http/binding/bind_test.go",
    "content": "package binding\n\nimport (\n\t\"errors\"\n\t\"io\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"reflect\"\n\t\"strings\"\n\t\"testing\"\n\n\tkratoserror \"github.com/go-kratos/kratos/v2/errors\"\n)\n\ntype (\n\tTestBind struct {\n\t\tName string `json:\"name\"`\n\t\tURL  string `json:\"url\"`\n\t}\n\tTestBind2 struct {\n\t\tAge int `json:\"age\"`\n\t}\n)\n\nfunc TestBindQuery(t *testing.T) {\n\ttype args struct {\n\t\tvars   url.Values\n\t\ttarget any\n\t}\n\n\ttests := []struct {\n\t\tname string\n\t\targs args\n\t\terr  error\n\t\twant any\n\t}{\n\t\t{\n\t\t\tname: \"test\",\n\t\t\targs: args{\n\t\t\t\tvars:   map[string][]string{\"name\": {\"kratos\"}, \"url\": {\"https://go-kratos.dev/\"}},\n\t\t\t\ttarget: &TestBind{},\n\t\t\t},\n\t\t\terr:  nil,\n\t\t\twant: &TestBind{\"kratos\", \"https://go-kratos.dev/\"},\n\t\t},\n\t\t{\n\t\t\tname: \"test1\",\n\t\t\targs: args{\n\t\t\t\tvars:   map[string][]string{\"age\": {\"kratos\"}, \"url\": {\"https://go-kratos.dev/\"}},\n\t\t\t\ttarget: &TestBind2{},\n\t\t\t},\n\t\t\terr: kratoserror.BadRequest(\"CODEC\", \"Field Namespace:age ERROR:Invalid Integer Value 'kratos' Type 'int' Namespace 'age'\"),\n\t\t},\n\t\t{\n\t\t\tname: \"test2\",\n\t\t\targs: args{\n\t\t\t\tvars:   map[string][]string{\"age\": {\"1\"}, \"url\": {\"https://go-kratos.dev/\"}},\n\t\t\t\ttarget: &TestBind2{},\n\t\t\t},\n\t\t\terr:  nil,\n\t\t\twant: &TestBind2{Age: 1},\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\terr := BindQuery(tt.args.vars, tt.args.target)\n\t\t\tif !kratoserror.Is(err, tt.err) {\n\t\t\t\tt.Fatalf(\"BindQuery() error = %v, err %v\", err, tt.err)\n\t\t\t}\n\t\t\tif err == nil && !reflect.DeepEqual(tt.args.target, tt.want) {\n\t\t\t\tt.Errorf(\"BindQuery() target = %v, want %v\", tt.args.target, tt.want)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestBindForm(t *testing.T) {\n\ttype args struct {\n\t\treq    *http.Request\n\t\ttarget any\n\t}\n\n\ttests := []struct {\n\t\tname string\n\t\targs args\n\t\terr  error\n\t\twant *TestBind\n\t}{\n\t\t{\n\t\t\tname: \"error not nil\",\n\t\t\targs: args{\n\t\t\t\treq:    &http.Request{Method: http.MethodPost},\n\t\t\t\ttarget: &TestBind{},\n\t\t\t},\n\t\t\terr:  errors.New(\"missing form body\"),\n\t\t\twant: nil,\n\t\t},\n\t\t{\n\t\t\tname: \"error is nil\",\n\t\t\targs: args{\n\t\t\t\treq: &http.Request{\n\t\t\t\t\tMethod: http.MethodPost,\n\t\t\t\t\tHeader: http.Header{\"Content-Type\": {\"application/x-www-form-urlencoded; param=value\"}},\n\t\t\t\t\tBody:   io.NopCloser(strings.NewReader(\"name=kratos&url=https://go-kratos.dev/\")),\n\t\t\t\t},\n\t\t\t\ttarget: &TestBind{},\n\t\t\t},\n\t\t\terr:  nil,\n\t\t\twant: &TestBind{\"kratos\", \"https://go-kratos.dev/\"},\n\t\t},\n\t\t{\n\t\t\tname: \"error BadRequest\",\n\t\t\targs: args{\n\t\t\t\treq: &http.Request{\n\t\t\t\t\tMethod: http.MethodPost,\n\t\t\t\t\tHeader: http.Header{\"Content-Type\": {\"application/x-www-form-urlencoded; param=value\"}},\n\t\t\t\t\tBody:   io.NopCloser(strings.NewReader(\"age=a\")),\n\t\t\t\t},\n\t\t\t\ttarget: &TestBind2{},\n\t\t\t},\n\t\t\terr:  kratoserror.BadRequest(\"CODEC\", \"Field Namespace:age ERROR:Invalid Integer Value 'a' Type 'int' Namespace 'age'\"),\n\t\t\twant: nil,\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\terr := BindForm(tt.args.req, tt.args.target)\n\t\t\tif !reflect.DeepEqual(err, tt.err) {\n\t\t\t\tt.Fatalf(\"BindForm() error = %v, err %v\", err, tt.err)\n\t\t\t}\n\t\t\tif err == nil && !reflect.DeepEqual(tt.args.target, tt.want) {\n\t\t\t\tt.Errorf(\"BindForm() target = %v, want %v\", tt.args.target, tt.want)\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "transport/http/binding/encode.go",
    "content": "package binding\n\nimport (\n\t\"reflect\"\n\t\"regexp\"\n\t\"strings\"\n\n\t\"google.golang.org/protobuf/proto\"\n\n\t\"github.com/go-kratos/kratos/v2/encoding/form\"\n)\n\nvar reg = regexp.MustCompile(`{[\\\\.\\w]+}`)\n\n// EncodeURL encode proto message to url path.\nfunc EncodeURL(pathTemplate string, msg any, needQuery bool) string {\n\tif msg == nil || (reflect.ValueOf(msg).Kind() == reflect.Ptr && reflect.ValueOf(msg).IsNil()) {\n\t\treturn pathTemplate\n\t}\n\n\tqueryParams, _ := form.EncodeValues(msg)\n\tpathParams := make(map[string]struct{})\n\tvar path string\n\tif strings.ContainsRune(pathTemplate, '{') {\n\t\tpath = reg.ReplaceAllStringFunc(pathTemplate, func(in string) string {\n\t\t\t// it's unreachable because the reg means that must have more than one char in {}\n\t\t\t// if len(in) < 4 { //nolint:mnd // ** explain the 4 number here :-) **\n\t\t\t//\treturn in\n\t\t\t// }\n\t\t\tkey := in[1 : len(in)-1]\n\t\t\tpathParams[key] = struct{}{}\n\t\t\treturn queryParams.Get(key)\n\t\t})\n\t} else {\n\t\tpath = pathTemplate\n\t}\n\n\tif !needQuery {\n\t\tif v, ok := msg.(proto.Message); ok {\n\t\t\tif query := form.EncodeFieldMask(v.ProtoReflect()); query != \"\" {\n\t\t\t\treturn path + \"?\" + query\n\t\t\t}\n\t\t}\n\t\treturn path\n\t}\n\tif len(queryParams) > 0 {\n\t\tfor key := range pathParams {\n\t\t\tdelete(queryParams, key)\n\t\t}\n\t\tif query := queryParams.Encode(); query != \"\" {\n\t\t\tpath += \"?\" + query\n\t\t}\n\t}\n\treturn path\n}\n"
  },
  {
    "path": "transport/http/binding/encode_test.go",
    "content": "package binding\n\nimport (\n\t\"testing\"\n\n\t\"google.golang.org/protobuf/types/known/fieldmaskpb\"\n\n\t\"github.com/go-kratos/kratos/v2/internal/testdata/binding\"\n)\n\nfunc TestEncodeURL(t *testing.T) {\n\ttests := []struct {\n\t\tpathTemplate string\n\t\trequest      *binding.HelloRequest\n\t\tneedQuery    bool\n\t\twant         string\n\t}{\n\t\t{\n\t\t\tpathTemplate: \"http://helloworld.Greeter/helloworld/{name}/sub/{sub.naming}\",\n\t\t\trequest:      &binding.HelloRequest{Name: \"test\", Sub: &binding.Sub{Name: \"2233!!!!\"}},\n\t\t\tneedQuery:    false,\n\t\t\twant:         \"http://helloworld.Greeter/helloworld/test/sub/2233!!!!\",\n\t\t},\n\t\t{\n\t\t\tpathTemplate: \"http://helloworld.Greeter/helloworld/{name}/sub/{sub.naming}\",\n\t\t\trequest:      nil,\n\t\t\tneedQuery:    false,\n\t\t\twant:         \"http://helloworld.Greeter/helloworld/{name}/sub/{sub.naming}\",\n\t\t},\n\t\t{\n\t\t\tpathTemplate: \"http://helloworld.Greeter/helloworld/{}/sub/{sub.naming}\",\n\t\t\trequest:      &binding.HelloRequest{Name: \"test\", Sub: &binding.Sub{Name: \"hello\"}},\n\t\t\tneedQuery:    false,\n\t\t\twant:         \"http://helloworld.Greeter/helloworld/{}/sub/hello\",\n\t\t},\n\t\t{\n\t\t\tpathTemplate: \"http://helloworld.Greeter/helloworld/{}/sub/{sub.name.cc}\",\n\t\t\trequest:      &binding.HelloRequest{Name: \"test\", Sub: &binding.Sub{Name: \"hello\"}},\n\t\t\tneedQuery:    false,\n\t\t\twant:         \"http://helloworld.Greeter/helloworld/{}/sub/\",\n\t\t},\n\t\t{\n\t\t\tpathTemplate: \"http://helloworld.Greeter/helloworld/{}/sub/{test_repeated}\",\n\t\t\trequest:      &binding.HelloRequest{Name: \"test\", Sub: &binding.Sub{Name: \"hello\"}, TestRepeated: []string{\"123\", \"456\"}},\n\t\t\tneedQuery:    false,\n\t\t\twant:         \"http://helloworld.Greeter/helloworld/{}/sub/123\",\n\t\t},\n\t\t{\n\t\t\tpathTemplate: \"http://helloworld.Greeter/helloworld/{name}/sub/{sub.naming}\",\n\t\t\trequest:      &binding.HelloRequest{Name: \"test\", Sub: &binding.Sub{Name: \"5566!!!\"}},\n\t\t\tneedQuery:    false,\n\t\t\twant:         \"http://helloworld.Greeter/helloworld/test/sub/5566!!!\",\n\t\t},\n\t\t{\n\t\t\tpathTemplate: \"http://helloworld.Greeter/helloworld/sub\",\n\t\t\trequest:      &binding.HelloRequest{Name: \"test\", Sub: &binding.Sub{Name: \"2233!!!\"}},\n\t\t\tneedQuery:    false,\n\t\t\twant:         \"http://helloworld.Greeter/helloworld/sub\",\n\t\t},\n\t\t{\n\t\t\tpathTemplate: \"http://helloworld.Greeter/helloworld/{name}/sub/{sub.name}\",\n\t\t\trequest:      &binding.HelloRequest{Name: \"test\"},\n\t\t\tneedQuery:    false,\n\t\t\twant:         \"http://helloworld.Greeter/helloworld/test/sub/\",\n\t\t},\n\t\t{\n\t\t\tpathTemplate: \"http://helloworld.Greeter/helloworld/{name}/sub\",\n\t\t\trequest:      &binding.HelloRequest{Name: \"go\", Sub: &binding.Sub{Name: \"kratos\"}},\n\t\t\tneedQuery:    true,\n\t\t\twant:         \"http://helloworld.Greeter/helloworld/go/sub?sub.naming=kratos\",\n\t\t},\n\t\t{\n\t\t\tpathTemplate: \"http://helloworld.Greeter/helloworld/sub/{sub.naming}\",\n\t\t\trequest:      &binding.HelloRequest{Sub: &binding.Sub{Name: \"kratos\"}, UpdateMask: &fieldmaskpb.FieldMask{Paths: []string{\"name\", \"sub.naming\"}}},\n\t\t\tneedQuery:    false,\n\t\t\twant:         \"http://helloworld.Greeter/helloworld/sub/kratos?updateMask=name,sub.naming\",\n\t\t},\n\t\t{\n\t\t\tpathTemplate: \"http://helloworld.Greeter/helloworld/sub/[{sub.naming}]\",\n\t\t\trequest:      &binding.HelloRequest{Sub: &binding.Sub{Name: \"kratos\"}},\n\t\t\tneedQuery:    false,\n\t\t\twant:         \"http://helloworld.Greeter/helloworld/sub/[kratos]\",\n\t\t},\n\t\t{\n\t\t\tpathTemplate: \"http://helloworld.Greeter/helloworld/[{name}]/sub/[{sub.naming}]\",\n\t\t\trequest:      &binding.HelloRequest{Name: \"test\", Sub: &binding.Sub{Name: \"kratos\"}},\n\t\t\tneedQuery:    false,\n\t\t\twant:         \"http://helloworld.Greeter/helloworld/[test]/sub/[kratos]\",\n\t\t},\n\t\t{\n\t\t\tpathTemplate: \"http://helloworld.Greeter/helloworld/[{}]/sub/[{sub.naming}]\",\n\t\t\trequest:      &binding.HelloRequest{Sub: &binding.Sub{Name: \"kratos\"}},\n\t\t\tneedQuery:    false,\n\t\t\twant:         \"http://helloworld.Greeter/helloworld/[{}]/sub/[kratos]\",\n\t\t},\n\t\t{\n\t\t\tpathTemplate: \"http://helloworld.Greeter/helloworld/[{}]/sub/[{sub.naming}]/{[]}\",\n\t\t\trequest:      &binding.HelloRequest{Sub: &binding.Sub{Name: \"kratos\"}},\n\t\t\tneedQuery:    false,\n\t\t\twant:         \"http://helloworld.Greeter/helloworld/[{}]/sub/[kratos]/{[]}\",\n\t\t},\n\t\t{\n\t\t\tpathTemplate: \"http://helloworld.Greeter/helloworld/{[sub]}/[{sub.naming}]\",\n\t\t\trequest:      &binding.HelloRequest{Sub: &binding.Sub{Name: \"kratos\"}},\n\t\t\tneedQuery:    false,\n\t\t\twant:         \"http://helloworld.Greeter/helloworld/{[sub]}/[kratos]\",\n\t\t},\n\t\t{\n\t\t\tpathTemplate: \"http://helloworld.Greeter/helloworld/{[name]}/[{sub.naming}]\",\n\t\t\trequest:      &binding.HelloRequest{Name: \"test\", Sub: &binding.Sub{Name: \"kratos\"}},\n\t\t\tneedQuery:    false,\n\t\t\twant:         \"http://helloworld.Greeter/helloworld/{[name]}/[kratos]\",\n\t\t},\n\t\t{\n\t\t\tpathTemplate: \"http://helloworld.Greeter/helloworld/{}/[]/[{sub.naming}]\",\n\t\t\trequest:      &binding.HelloRequest{Sub: &binding.Sub{Name: \"kratos\"}},\n\t\t\tneedQuery:    false,\n\t\t\twant:         \"http://helloworld.Greeter/helloworld/{}/[]/[kratos]\",\n\t\t},\n\t}\n\n\tfor _, test := range tests {\n\t\tif path := EncodeURL(test.pathTemplate, test.request, test.needQuery); path != test.want {\n\t\t\tt.Fatalf(\"want: %s, got: %s\", test.want, path)\n\t\t}\n\t}\n}\n\nfunc BenchmarkEncodeURL(b *testing.B) {\n\tbenchmarks := []struct {\n\t\tname         string\n\t\tpathTemplate string\n\t\tmsg          *binding.HelloRequest\n\t\tneedQuery    bool\n\t}{\n\t\t{\n\t\t\tname:         \"NoParams\",\n\t\t\tpathTemplate: \"http://helloworld.Greeter/helloworld/sub\",\n\t\t\tmsg: &binding.HelloRequest{\n\t\t\t\tName: \"test\",\n\t\t\t\tSub:  &binding.Sub{Name: \"kratos\"},\n\t\t\t},\n\t\t\tneedQuery: false,\n\t\t},\n\t\t{\n\t\t\tname:         \"NoParamsWithQuery\",\n\t\t\tpathTemplate: \"http://helloworld.Greeter/helloworld/sub\",\n\t\t\tmsg: &binding.HelloRequest{\n\t\t\t\tName: \"test\",\n\t\t\t\tSub:  &binding.Sub{Name: \"kratos\"},\n\t\t\t\tUpdateMask: &fieldmaskpb.FieldMask{\n\t\t\t\t\tPaths: []string{\"name\", \"sub.naming\"},\n\t\t\t\t},\n\t\t\t},\n\t\t\tneedQuery: true,\n\t\t},\n\t\t{\n\t\t\tname:         \"WithParams\",\n\t\t\tpathTemplate: \"http://helloworld.Greeter/helloworld/{name}/sub/{sub.naming}\",\n\t\t\tmsg: &binding.HelloRequest{\n\t\t\t\tName: \"test\",\n\t\t\t\tSub:  &binding.Sub{Name: \"kratos\"},\n\t\t\t},\n\t\t\tneedQuery: false,\n\t\t},\n\t\t{\n\t\t\tname:         \"WithParamsAndQuery\",\n\t\t\tpathTemplate: \"http://helloworld.Greeter/helloworld/{name}/sub/{sub.naming}\",\n\t\t\tmsg: &binding.HelloRequest{\n\t\t\t\tName: \"test\",\n\t\t\t\tSub:  &binding.Sub{Name: \"kratos\"},\n\t\t\t\tUpdateMask: &fieldmaskpb.FieldMask{\n\t\t\t\t\tPaths: []string{\"name\", \"sub.naming\"},\n\t\t\t\t},\n\t\t\t},\n\t\t\tneedQuery: true,\n\t\t},\n\t}\n\n\tfor _, bm := range benchmarks {\n\t\tb.Run(bm.name, func(b *testing.B) {\n\t\t\tb.ReportAllocs()\n\t\t\tfor i := 0; i < b.N; i++ {\n\t\t\t\tEncodeURL(bm.pathTemplate, bm.msg, bm.needQuery)\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "transport/http/calloption.go",
    "content": "package http\n\nimport (\n\t\"net/http\"\n)\n\n// CallOption configures a Call before it starts or extracts information from\n// a Call after it completes.\ntype CallOption interface {\n\t// before is called before the call is sent to any server. If before\n\t// returns a non-nil error, the RPC fails with that error.\n\tbefore(*callInfo) error\n\n\t// after is called after the call has completed. after cannot return an\n\t// error, so any failures should be reported via output parameters.\n\tafter(*callInfo, *csAttempt)\n}\n\ntype callInfo struct {\n\tcontentType   string\n\toperation     string\n\tpathTemplate  string\n\theaderCarrier *http.Header\n}\n\n// EmptyCallOption does not alter the Call configuration.\n// It can be embedded in another structure to carry satellite data for use\n// by interceptors.\ntype EmptyCallOption struct{}\n\nfunc (EmptyCallOption) before(*callInfo) error      { return nil }\nfunc (EmptyCallOption) after(*callInfo, *csAttempt) {}\n\ntype csAttempt struct {\n\tres *http.Response\n}\n\n// ContentType with request content type.\nfunc ContentType(contentType string) CallOption {\n\treturn ContentTypeCallOption{ContentType: contentType}\n}\n\n// ContentTypeCallOption is BodyCallOption\ntype ContentTypeCallOption struct {\n\tEmptyCallOption\n\tContentType string\n}\n\nfunc (o ContentTypeCallOption) before(c *callInfo) error {\n\tc.contentType = o.ContentType\n\treturn nil\n}\n\nfunc defaultCallInfo(path string) callInfo {\n\treturn callInfo{\n\t\tcontentType:  \"application/json\",\n\t\toperation:    path,\n\t\tpathTemplate: path,\n\t}\n}\n\n// Operation is serviceMethod call option\nfunc Operation(operation string) CallOption {\n\treturn OperationCallOption{Operation: operation}\n}\n\n// OperationCallOption is set ServiceMethod for client call\ntype OperationCallOption struct {\n\tEmptyCallOption\n\tOperation string\n}\n\nfunc (o OperationCallOption) before(c *callInfo) error {\n\tc.operation = o.Operation\n\treturn nil\n}\n\n// PathTemplate is http path template\nfunc PathTemplate(pattern string) CallOption {\n\treturn PathTemplateCallOption{Pattern: pattern}\n}\n\n// PathTemplateCallOption is set path template for client call\ntype PathTemplateCallOption struct {\n\tEmptyCallOption\n\tPattern string\n}\n\nfunc (o PathTemplateCallOption) before(c *callInfo) error {\n\tc.pathTemplate = o.Pattern\n\treturn nil\n}\n\n// Header returns a CallOptions that retrieves the http response header\n// from server reply.\nfunc Header(header *http.Header) CallOption {\n\treturn HeaderCallOption{header: header}\n}\n\n// HeaderCallOption is retrieve response header for client call\ntype HeaderCallOption struct {\n\tEmptyCallOption\n\theader *http.Header\n}\n\nfunc (o HeaderCallOption) before(c *callInfo) error {\n\tc.headerCarrier = o.header\n\treturn nil\n}\n\nfunc (o HeaderCallOption) after(_ *callInfo, cs *csAttempt) {\n\tif cs.res != nil && cs.res.Header != nil {\n\t\t*o.header = cs.res.Header\n\t}\n}\n"
  },
  {
    "path": "transport/http/calloption_test.go",
    "content": "package http\n\nimport (\n\t\"net/http\"\n\t\"reflect\"\n\t\"testing\"\n)\n\nfunc TestEmptyCallOptions(t *testing.T) {\n\te := EmptyCallOption{}\n\tif e.before(&callInfo{}) != nil {\n\t\tt.Error(\"EmptyCallOption should be ignored\")\n\t}\n\te.after(&callInfo{}, &csAttempt{})\n}\n\nfunc TestContentType(t *testing.T) {\n\tif !reflect.DeepEqual(ContentType(\"aaa\").(ContentTypeCallOption).ContentType, \"aaa\") {\n\t\tt.Errorf(\"want: %v,got: %v\", \"aaa\", ContentType(\"aaa\").(ContentTypeCallOption).ContentType)\n\t}\n}\n\nfunc TestContentTypeCallOption_before(t *testing.T) {\n\tc := &callInfo{}\n\terr := ContentType(\"aaa\").before(c)\n\tif err != nil {\n\t\tt.Errorf(\"unexpected error: %v\", err)\n\t}\n\tif !reflect.DeepEqual(\"aaa\", c.contentType) {\n\t\tt.Errorf(\"want: %v, got: %v\", \"aaa\", c.contentType)\n\t}\n}\n\nfunc TestDefaultCallInfo(t *testing.T) {\n\tpath := \"hi\"\n\trv := defaultCallInfo(path)\n\tif !reflect.DeepEqual(path, rv.pathTemplate) {\n\t\tt.Errorf(\"expect %v, got %v\", path, rv.pathTemplate)\n\t}\n\tif !reflect.DeepEqual(path, rv.operation) {\n\t\tt.Errorf(\"expect %v, got %v\", path, rv.operation)\n\t}\n\tif !reflect.DeepEqual(\"application/json\", rv.contentType) {\n\t\tt.Errorf(\"expect %v, got %v\", \"application/json\", rv.contentType)\n\t}\n}\n\nfunc TestOperation(t *testing.T) {\n\tif !reflect.DeepEqual(\"aaa\", Operation(\"aaa\").(OperationCallOption).Operation) {\n\t\tt.Errorf(\"want: %v,got: %v\", \"aaa\", Operation(\"aaa\").(OperationCallOption).Operation)\n\t}\n}\n\nfunc TestOperationCallOption_before(t *testing.T) {\n\tc := &callInfo{}\n\terr := Operation(\"aaa\").before(c)\n\tif err != nil {\n\t\tt.Errorf(\"unexpected error: %v\", err)\n\t}\n\tif !reflect.DeepEqual(\"aaa\", c.operation) {\n\t\tt.Errorf(\"want: %v, got: %v\", \"aaa\", c.operation)\n\t}\n}\n\nfunc TestPathTemplate(t *testing.T) {\n\tif !reflect.DeepEqual(\"aaa\", PathTemplate(\"aaa\").(PathTemplateCallOption).Pattern) {\n\t\tt.Errorf(\"want: %v,got: %v\", \"aaa\", PathTemplate(\"aaa\").(PathTemplateCallOption).Pattern)\n\t}\n}\n\nfunc TestPathTemplateCallOption_before(t *testing.T) {\n\tc := &callInfo{}\n\terr := PathTemplate(\"aaa\").before(c)\n\tif err != nil {\n\t\tt.Errorf(\"unexpected error: %v\", err)\n\t}\n\tif !reflect.DeepEqual(\"aaa\", c.pathTemplate) {\n\t\tt.Errorf(\"want: %v, got: %v\", \"aaa\", c.pathTemplate)\n\t}\n}\n\nfunc TestHeader(t *testing.T) {\n\th := http.Header{\"A\": []string{\"123\"}}\n\tif !reflect.DeepEqual(Header(&h).(HeaderCallOption).header.Get(\"A\"), \"123\") {\n\t\tt.Errorf(\"want: %v,got: %v\", \"123\", Header(&h).(HeaderCallOption).header.Get(\"A\"))\n\t}\n}\n\nfunc TestHeaderCallOption_before(t *testing.T) {\n\th := http.Header{\"A\": []string{\"123\"}}\n\tc := &callInfo{}\n\to := Header(&h)\n\terr := o.before(c)\n\tif err != nil {\n\t\tt.Errorf(\"unexpected error: %v\", err)\n\t}\n\tif !reflect.DeepEqual(&h, c.headerCarrier) {\n\t\tt.Errorf(\"want: %v,got: %v\", &h, o.(HeaderCallOption).header)\n\t}\n}\n\nfunc TestHeaderCallOption_after(t *testing.T) {\n\th := http.Header{\"A\": []string{\"123\"}}\n\tc := &callInfo{}\n\tcs := &csAttempt{res: &http.Response{Header: h}}\n\to := Header(&h)\n\to.after(c, cs)\n\tif !reflect.DeepEqual(&h, o.(HeaderCallOption).header) {\n\t\tt.Errorf(\"want: %v,got: %v\", &h, o.(HeaderCallOption).header)\n\t}\n}\n"
  },
  {
    "path": "transport/http/client.go",
    "content": "package http\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"crypto/tls\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"time\"\n\n\t\"github.com/go-kratos/kratos/v2/encoding\"\n\t\"github.com/go-kratos/kratos/v2/errors\"\n\t\"github.com/go-kratos/kratos/v2/internal/host\"\n\t\"github.com/go-kratos/kratos/v2/internal/httputil\"\n\t\"github.com/go-kratos/kratos/v2/middleware\"\n\t\"github.com/go-kratos/kratos/v2/registry\"\n\t\"github.com/go-kratos/kratos/v2/selector\"\n\t\"github.com/go-kratos/kratos/v2/selector/wrr\"\n\t\"github.com/go-kratos/kratos/v2/transport\"\n)\n\nfunc init() {\n\tif selector.GlobalSelector() == nil {\n\t\tselector.SetGlobalSelector(wrr.NewBuilder())\n\t}\n}\n\n// DecodeErrorFunc is decode error func.\ntype DecodeErrorFunc func(ctx context.Context, res *http.Response) error\n\n// EncodeRequestFunc is request encode func.\ntype EncodeRequestFunc func(ctx context.Context, contentType string, in any) (body []byte, err error)\n\n// DecodeResponseFunc is response decode func.\ntype DecodeResponseFunc func(ctx context.Context, res *http.Response, out any) error\n\n// ClientOption is HTTP client option.\ntype ClientOption func(*clientOptions)\n\n// Client is an HTTP transport client.\ntype clientOptions struct {\n\tctx          context.Context\n\ttlsConf      *tls.Config\n\ttimeout      time.Duration\n\tendpoint     string\n\tuserAgent    string\n\tencoder      EncodeRequestFunc\n\tdecoder      DecodeResponseFunc\n\terrorDecoder DecodeErrorFunc\n\ttransport    http.RoundTripper\n\tnodeFilters  []selector.NodeFilter\n\tdiscovery    registry.Discovery\n\tmiddleware   []middleware.Middleware\n\tblock        bool\n\tsubsetSize   int\n}\n\n// WithSubset with client discovery subset size.\n// zero value means subset filter disabled\nfunc WithSubset(size int) ClientOption {\n\treturn func(o *clientOptions) {\n\t\to.subsetSize = size\n\t}\n}\n\n// WithTransport with client transport.\nfunc WithTransport(trans http.RoundTripper) ClientOption {\n\treturn func(o *clientOptions) {\n\t\to.transport = trans\n\t}\n}\n\n// WithTimeout with client request timeout.\nfunc WithTimeout(d time.Duration) ClientOption {\n\treturn func(o *clientOptions) {\n\t\to.timeout = d\n\t}\n}\n\n// WithUserAgent with client user agent.\nfunc WithUserAgent(ua string) ClientOption {\n\treturn func(o *clientOptions) {\n\t\to.userAgent = ua\n\t}\n}\n\n// WithMiddleware with client middleware.\nfunc WithMiddleware(m ...middleware.Middleware) ClientOption {\n\treturn func(o *clientOptions) {\n\t\to.middleware = m\n\t}\n}\n\n// WithEndpoint with client addr.\nfunc WithEndpoint(endpoint string) ClientOption {\n\treturn func(o *clientOptions) {\n\t\to.endpoint = endpoint\n\t}\n}\n\n// WithRequestEncoder with client request encoder.\nfunc WithRequestEncoder(encoder EncodeRequestFunc) ClientOption {\n\treturn func(o *clientOptions) {\n\t\to.encoder = encoder\n\t}\n}\n\n// WithResponseDecoder with client response decoder.\nfunc WithResponseDecoder(decoder DecodeResponseFunc) ClientOption {\n\treturn func(o *clientOptions) {\n\t\to.decoder = decoder\n\t}\n}\n\n// WithErrorDecoder with client error decoder.\nfunc WithErrorDecoder(errorDecoder DecodeErrorFunc) ClientOption {\n\treturn func(o *clientOptions) {\n\t\to.errorDecoder = errorDecoder\n\t}\n}\n\n// WithDiscovery with client discovery.\nfunc WithDiscovery(d registry.Discovery) ClientOption {\n\treturn func(o *clientOptions) {\n\t\to.discovery = d\n\t}\n}\n\n// WithNodeFilter with select filters\nfunc WithNodeFilter(filters ...selector.NodeFilter) ClientOption {\n\treturn func(o *clientOptions) {\n\t\to.nodeFilters = filters\n\t}\n}\n\n// WithBlock with client block.\nfunc WithBlock() ClientOption {\n\treturn func(o *clientOptions) {\n\t\to.block = true\n\t}\n}\n\n// WithTLSConfig with tls config.\nfunc WithTLSConfig(c *tls.Config) ClientOption {\n\treturn func(o *clientOptions) {\n\t\to.tlsConf = c\n\t}\n}\n\n// Client is an HTTP client.\ntype Client struct {\n\topts     clientOptions\n\ttarget   *Target\n\tr        *resolver\n\tcc       *http.Client\n\tinsecure bool\n\tselector selector.Selector\n}\n\n// NewClient returns an HTTP client.\nfunc NewClient(ctx context.Context, opts ...ClientOption) (*Client, error) {\n\toptions := clientOptions{\n\t\tctx:          ctx,\n\t\ttimeout:      2000 * time.Millisecond,\n\t\tencoder:      DefaultRequestEncoder,\n\t\tdecoder:      DefaultResponseDecoder,\n\t\terrorDecoder: DefaultErrorDecoder,\n\t\ttransport:    http.DefaultTransport,\n\t\tsubsetSize:   25,\n\t}\n\tfor _, o := range opts {\n\t\to(&options)\n\t}\n\tif options.tlsConf != nil {\n\t\tif tr, ok := options.transport.(*http.Transport); ok {\n\t\t\ttr.TLSClientConfig = options.tlsConf\n\t\t}\n\t}\n\tinsecure := options.tlsConf == nil\n\ttarget, err := parseTarget(options.endpoint, insecure)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tselector := selector.GlobalSelector().Build()\n\tvar r *resolver\n\tif options.discovery != nil {\n\t\tif target.Scheme == \"discovery\" {\n\t\t\tif r, err = newResolver(ctx, options.discovery, target, selector, options.block, insecure, options.subsetSize); err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"[http client] new resolver failed for endpoint %q: %w\", options.endpoint, err)\n\t\t\t}\n\t\t} else if _, _, err := host.ExtractHostPort(options.endpoint); err != nil {\n\t\t\treturn nil, fmt.Errorf(\"[http client] invalid endpoint format %q: %w\", options.endpoint, err)\n\t\t}\n\t}\n\treturn &Client{\n\t\topts:     options,\n\t\ttarget:   target,\n\t\tinsecure: insecure,\n\t\tr:        r,\n\t\tcc: &http.Client{\n\t\t\tTimeout:   options.timeout,\n\t\t\tTransport: options.transport,\n\t\t},\n\t\tselector: selector,\n\t}, nil\n}\n\n// Invoke makes a rpc call procedure for remote service.\nfunc (client *Client) Invoke(ctx context.Context, method, path string, args any, reply any, opts ...CallOption) error {\n\tvar (\n\t\tcontentType string\n\t\tbody        io.Reader\n\t)\n\tc := defaultCallInfo(path)\n\tfor _, o := range opts {\n\t\tif err := o.before(&c); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tif args != nil {\n\t\tdata, err := client.opts.encoder(ctx, c.contentType, args)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tcontentType = c.contentType\n\t\tbody = bytes.NewReader(data)\n\t}\n\turl := fmt.Sprintf(\"%s://%s%s\", client.target.Scheme, client.target.Authority, path)\n\treq, err := http.NewRequest(method, url, body)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif c.headerCarrier != nil {\n\t\treq.Header = *c.headerCarrier\n\t}\n\n\tif contentType != \"\" {\n\t\treq.Header.Set(\"Content-Type\", c.contentType)\n\t}\n\tif client.opts.userAgent != \"\" {\n\t\treq.Header.Set(\"User-Agent\", client.opts.userAgent)\n\t}\n\tctx = transport.NewClientContext(ctx, &Transport{\n\t\tendpoint:     client.opts.endpoint,\n\t\treqHeader:    headerCarrier(req.Header),\n\t\toperation:    c.operation,\n\t\trequest:      req,\n\t\tpathTemplate: c.pathTemplate,\n\t})\n\treturn client.invoke(ctx, req, args, reply, c, opts...)\n}\n\nfunc (client *Client) invoke(ctx context.Context, req *http.Request, args any, reply any, c callInfo, opts ...CallOption) error {\n\th := func(ctx context.Context, _ any) (any, error) {\n\t\tres, err := client.do(req.WithContext(ctx))\n\t\tif res != nil {\n\t\t\tcs := csAttempt{res: res}\n\t\t\tfor _, o := range opts {\n\t\t\t\to.after(&c, &cs)\n\t\t\t}\n\t\t}\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tdefer res.Body.Close()\n\t\tif err := client.opts.decoder(ctx, res, reply); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\treturn reply, nil\n\t}\n\tvar p selector.Peer\n\tctx = selector.NewPeerContext(ctx, &p)\n\tif len(client.opts.middleware) > 0 {\n\t\th = middleware.Chain(client.opts.middleware...)(h)\n\t}\n\t_, err := h(ctx, args)\n\treturn err\n}\n\n// Do send an HTTP request and decodes the body of response into target.\n// returns an error (of type *Error) if the response status code is not 2xx.\nfunc (client *Client) Do(req *http.Request, opts ...CallOption) (*http.Response, error) {\n\tc := defaultCallInfo(req.URL.Path)\n\tfor _, o := range opts {\n\t\tif err := o.before(&c); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\n\treturn client.do(req)\n}\n\nfunc (client *Client) do(req *http.Request) (*http.Response, error) {\n\tvar done func(context.Context, selector.DoneInfo)\n\tif client.r != nil {\n\t\tvar (\n\t\t\terr  error\n\t\t\tnode selector.Node\n\t\t)\n\t\tif node, done, err = client.selector.Select(req.Context(), selector.WithNodeFilter(client.opts.nodeFilters...)); err != nil {\n\t\t\treturn nil, errors.ServiceUnavailable(\"NODE_NOT_FOUND\", err.Error())\n\t\t}\n\t\tif client.insecure {\n\t\t\treq.URL.Scheme = \"http\"\n\t\t} else {\n\t\t\treq.URL.Scheme = \"https\"\n\t\t}\n\t\treq.URL.Host = node.Address()\n\t\treq.Host = node.Address()\n\t}\n\tresp, err := client.cc.Do(req)\n\tif err == nil {\n\t\tt, ok := transport.FromClientContext(req.Context())\n\t\tif ok {\n\t\t\tht, ok := t.(*Transport)\n\t\t\tif ok {\n\t\t\t\tht.replyHeader = headerCarrier(resp.Header)\n\t\t\t}\n\t\t}\n\t\terr = client.opts.errorDecoder(req.Context(), resp)\n\t}\n\tif done != nil {\n\t\tdone(req.Context(), selector.DoneInfo{Err: err})\n\t}\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn resp, nil\n}\n\n// Close tears down the Transport and all underlying connections.\nfunc (client *Client) Close() error {\n\tif client.r != nil {\n\t\treturn client.r.Close()\n\t}\n\treturn nil\n}\n\n// DefaultRequestEncoder is an HTTP request encoder.\nfunc DefaultRequestEncoder(_ context.Context, contentType string, in any) ([]byte, error) {\n\tname := httputil.ContentSubtype(contentType)\n\tbody, err := encoding.GetCodec(name).Marshal(in)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn body, err\n}\n\n// DefaultResponseDecoder is an HTTP response decoder.\nfunc DefaultResponseDecoder(_ context.Context, res *http.Response, v any) error {\n\tdefer res.Body.Close()\n\tdata, err := io.ReadAll(res.Body)\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn CodecForResponse(res).Unmarshal(data, v)\n}\n\n// DefaultErrorDecoder is an HTTP error decoder.\nfunc DefaultErrorDecoder(_ context.Context, res *http.Response) error {\n\tif res.StatusCode >= 200 && res.StatusCode <= 299 {\n\t\treturn nil\n\t}\n\tdefer res.Body.Close()\n\tdata, err := io.ReadAll(res.Body)\n\tif err == nil {\n\t\te := new(errors.Error)\n\t\tif err = CodecForResponse(res).Unmarshal(data, e); err == nil {\n\t\t\te.Code = int32(res.StatusCode)\n\t\t\treturn e\n\t\t}\n\t}\n\treturn errors.Newf(res.StatusCode, errors.UnknownReason, \"\").WithCause(err)\n}\n\n// CodecForResponse get encoding.Codec via http.Response\nfunc CodecForResponse(r *http.Response) encoding.Codec {\n\tcodec := encoding.GetCodec(httputil.ContentSubtype(r.Header.Get(\"Content-Type\")))\n\tif codec != nil {\n\t\treturn codec\n\t}\n\treturn encoding.GetCodec(\"json\")\n}\n"
  },
  {
    "path": "transport/http/client_test.go",
    "content": "package http\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"crypto/tls\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"log\"\n\t\"net/http\"\n\t\"reflect\"\n\t\"strconv\"\n\t\"testing\"\n\t\"time\"\n\n\tkratoserrors \"github.com/go-kratos/kratos/v2/errors\"\n\t\"github.com/go-kratos/kratos/v2/middleware\"\n\t\"github.com/go-kratos/kratos/v2/registry\"\n\t\"github.com/go-kratos/kratos/v2/selector\"\n)\n\ntype mockRoundTripper struct{}\n\nfunc (rt *mockRoundTripper) RoundTrip(_ *http.Request) (resp *http.Response, err error) {\n\treturn\n}\n\ntype mockCallOption struct {\n\tneedErr bool\n}\n\nfunc (x *mockCallOption) before(_ *callInfo) error {\n\tif x.needErr {\n\t\treturn errors.New(\"option need return err\")\n\t}\n\treturn nil\n}\n\nfunc (x *mockCallOption) after(_ *callInfo, _ *csAttempt) {\n\tlog.Println(\"run in mockCallOption.after\")\n}\n\nfunc TestWithSubset(t *testing.T) {\n\tco := &clientOptions{}\n\to := WithSubset(1)\n\to(co)\n\tif co.subsetSize != 1 {\n\t\tt.Error(\"expected subset size to be 1\")\n\t}\n}\n\nfunc TestWithTransport(t *testing.T) {\n\tov := &mockRoundTripper{}\n\to := WithTransport(ov)\n\tco := &clientOptions{}\n\to(co)\n\tif !reflect.DeepEqual(co.transport, ov) {\n\t\tt.Errorf(\"expected transport to be %v, got %v\", ov, co.transport)\n\t}\n}\n\nfunc TestWithTimeout(t *testing.T) {\n\tov := 1 * time.Second\n\to := WithTimeout(ov)\n\tco := &clientOptions{}\n\to(co)\n\tif !reflect.DeepEqual(co.timeout, ov) {\n\t\tt.Errorf(\"expected timeout to be %v, got %v\", ov, co.timeout)\n\t}\n}\n\nfunc TestWithBlock(t *testing.T) {\n\to := WithBlock()\n\tco := &clientOptions{}\n\to(co)\n\tif !co.block {\n\t\tt.Errorf(\"expected block to be true, got %v\", co.block)\n\t}\n}\n\nfunc TestWithTLSConfig(t *testing.T) {\n\tov := &tls.Config{}\n\to := WithTLSConfig(ov)\n\tco := &clientOptions{}\n\to(co)\n\tif !reflect.DeepEqual(co.tlsConf, ov) {\n\t\tt.Errorf(\"expected tls config to be %v, got %v\", ov, co.tlsConf)\n\t}\n}\n\nfunc TestWithUserAgent(t *testing.T) {\n\tov := \"kratos\"\n\to := WithUserAgent(ov)\n\tco := &clientOptions{}\n\to(co)\n\tif !reflect.DeepEqual(co.userAgent, ov) {\n\t\tt.Errorf(\"expected user agent to be %v, got %v\", ov, co.userAgent)\n\t}\n}\n\nfunc TestWithMiddleware(t *testing.T) {\n\to := &clientOptions{}\n\tv := []middleware.Middleware{\n\t\tfunc(middleware.Handler) middleware.Handler { return nil },\n\t}\n\tWithMiddleware(v...)(o)\n\tif !reflect.DeepEqual(o.middleware, v) {\n\t\tt.Errorf(\"expected middleware to be %v, got %v\", v, o.middleware)\n\t}\n}\n\nfunc TestWithEndpoint(t *testing.T) {\n\tov := \"some-endpoint\"\n\to := WithEndpoint(ov)\n\tco := &clientOptions{}\n\to(co)\n\tif !reflect.DeepEqual(co.endpoint, ov) {\n\t\tt.Errorf(\"expected endpoint to be %v, got %v\", ov, co.endpoint)\n\t}\n}\n\nfunc TestWithRequestEncoder(t *testing.T) {\n\to := &clientOptions{}\n\tv := func(context.Context, string, any) (body []byte, err error) {\n\t\treturn nil, nil\n\t}\n\tWithRequestEncoder(v)(o)\n\tif o.encoder == nil {\n\t\tt.Errorf(\"expected encoder to be not nil\")\n\t}\n}\n\nfunc TestWithResponseDecoder(t *testing.T) {\n\to := &clientOptions{}\n\tv := func(context.Context, *http.Response, any) error { return nil }\n\tWithResponseDecoder(v)(o)\n\tif o.decoder == nil {\n\t\tt.Errorf(\"expected encoder to be not nil\")\n\t}\n}\n\nfunc TestWithErrorDecoder(t *testing.T) {\n\to := &clientOptions{}\n\tv := func(context.Context, *http.Response) error { return nil }\n\tWithErrorDecoder(v)(o)\n\tif o.errorDecoder == nil {\n\t\tt.Errorf(\"expected encoder to be not nil\")\n\t}\n}\n\ntype mockDiscovery struct{}\n\nfunc (*mockDiscovery) GetService(_ context.Context, _ string) ([]*registry.ServiceInstance, error) {\n\treturn nil, nil\n}\n\nfunc (*mockDiscovery) Watch(_ context.Context, _ string) (registry.Watcher, error) {\n\treturn &mockWatcher{}, nil\n}\n\ntype mockWatcher struct{}\n\nfunc (m *mockWatcher) Next() ([]*registry.ServiceInstance, error) {\n\tinstance := &registry.ServiceInstance{\n\t\tID:        \"1\",\n\t\tName:      \"kratos\",\n\t\tVersion:   \"v1\",\n\t\tMetadata:  map[string]string{},\n\t\tEndpoints: []string{fmt.Sprintf(\"http://127.0.0.1:9001?isSecure=%s\", strconv.FormatBool(false))},\n\t}\n\ttime.Sleep(time.Millisecond * 500)\n\treturn []*registry.ServiceInstance{instance}, nil\n}\n\nfunc (*mockWatcher) Stop() error {\n\treturn nil\n}\n\nfunc TestWithDiscovery(t *testing.T) {\n\tov := &mockDiscovery{}\n\to := WithDiscovery(ov)\n\tco := &clientOptions{}\n\to(co)\n\tif !reflect.DeepEqual(co.discovery, ov) {\n\t\tt.Errorf(\"expected discovery to be %v, got %v\", ov, co.discovery)\n\t}\n}\n\nfunc TestWithNodeFilter(t *testing.T) {\n\tov := func(context.Context, []selector.Node) []selector.Node {\n\t\treturn []selector.Node{&selector.DefaultNode{}}\n\t}\n\to := WithNodeFilter(ov)\n\tco := &clientOptions{}\n\to(co)\n\tfor _, n := range co.nodeFilters {\n\t\tret := n(context.Background(), nil)\n\t\tif len(ret) != 1 {\n\t\t\tt.Errorf(\"expected node  length to be 1, got %v\", len(ret))\n\t\t}\n\t}\n}\n\nfunc TestDefaultRequestEncoder(t *testing.T) {\n\tr, _ := http.NewRequest(http.MethodPost, \"\", io.NopCloser(bytes.NewBufferString(`{\"a\":\"1\", \"b\": 2}`)))\n\tr.Header.Set(\"Content-Type\", \"application/xml\")\n\n\tv1 := &struct {\n\t\tA string `json:\"a\"`\n\t\tB int64  `json:\"b\"`\n\t}{\"a\", 1}\n\tb, err := DefaultRequestEncoder(context.TODO(), \"application/json\", v1)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tv1b := &struct {\n\t\tA string `json:\"a\"`\n\t\tB int64  `json:\"b\"`\n\t}{}\n\terr = json.Unmarshal(b, v1b)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif !reflect.DeepEqual(v1b, v1) {\n\t\tt.Errorf(\"expected %v, got %v\", v1, v1b)\n\t}\n}\n\nfunc TestDefaultResponseDecoder(t *testing.T) {\n\tresp1 := &http.Response{\n\t\tHeader:     make(http.Header),\n\t\tStatusCode: 200,\n\t\tBody:       io.NopCloser(bytes.NewBufferString(`{\"a\":\"1\", \"b\": 2}`)),\n\t}\n\tv1 := &struct {\n\t\tA string `json:\"a\"`\n\t\tB int64  `json:\"b\"`\n\t}{}\n\terr := DefaultResponseDecoder(context.TODO(), resp1, &v1)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif v1.A != \"1\" {\n\t\tt.Errorf(\"expected %v, got %v\", \"1\", v1.A)\n\t}\n\tif v1.B != int64(2) {\n\t\tt.Errorf(\"expected %v, got %v\", 2, v1.B)\n\t}\n\n\tresp2 := &http.Response{\n\t\tHeader:     make(http.Header),\n\t\tStatusCode: 200,\n\t\tBody:       io.NopCloser(bytes.NewBufferString(\"{badjson}\")),\n\t}\n\tv2 := &struct {\n\t\tA string `json:\"a\"`\n\t\tB int64  `json:\"b\"`\n\t}{}\n\terr = DefaultResponseDecoder(context.TODO(), resp2, &v2)\n\tsyntaxErr := &json.SyntaxError{}\n\tif !errors.As(err, &syntaxErr) {\n\t\tt.Errorf(\"expected %v, got %v\", syntaxErr, err)\n\t}\n}\n\nfunc TestDefaultErrorDecoder(t *testing.T) {\n\tfor i := 200; i < 300; i++ {\n\t\tresp := &http.Response{Header: make(http.Header), StatusCode: i}\n\t\tif DefaultErrorDecoder(context.TODO(), resp) != nil {\n\t\t\tt.Errorf(\"expected no error, got %v\", DefaultErrorDecoder(context.TODO(), resp))\n\t\t}\n\t}\n\tresp1 := &http.Response{\n\t\tHeader:     make(http.Header),\n\t\tStatusCode: 300,\n\t\tBody:       io.NopCloser(bytes.NewBufferString(\"{\\\"foo\\\":\\\"bar\\\"}\")),\n\t}\n\tif DefaultErrorDecoder(context.TODO(), resp1) == nil {\n\t\tt.Errorf(\"expected error, got nil\")\n\t}\n\n\tresp2 := &http.Response{\n\t\tHeader:     make(http.Header),\n\t\tStatusCode: 500,\n\t\tBody:       io.NopCloser(bytes.NewBufferString(`{\"code\":54321, \"message\": \"hi\", \"reason\": \"FOO\"}`)),\n\t}\n\terr := DefaultErrorDecoder(context.TODO(), resp2)\n\tif err == nil {\n\t\tt.Errorf(\"expected error, got nil\")\n\t}\n\tif err.(*kratoserrors.Error).Code != int32(500) {\n\t\tt.Errorf(\"expected %v, got %v\", 500, err.(*kratoserrors.Error).Code)\n\t}\n\tif err.(*kratoserrors.Error).Message != \"hi\" {\n\t\tt.Errorf(\"expected %v, got %v\", \"hi\", err.(*kratoserrors.Error).Message)\n\t}\n\tif err.(*kratoserrors.Error).Reason != \"FOO\" {\n\t\tt.Errorf(\"expected %v, got %v\", \"FOO\", err.(*kratoserrors.Error).Reason)\n\t}\n}\n\nfunc TestCodecForResponse(t *testing.T) {\n\tresp := &http.Response{Header: make(http.Header)}\n\tresp.Header.Set(\"Content-Type\", \"application/xml\")\n\tc := CodecForResponse(resp)\n\tif !reflect.DeepEqual(\"xml\", c.Name()) {\n\t\tt.Errorf(\"expected %v, got %v\", \"xml\", c.Name())\n\t}\n}\n\nfunc TestNewClient(t *testing.T) {\n\t_, err := NewClient(context.Background(), WithEndpoint(\"127.0.0.1:8888\"))\n\tif err != nil {\n\t\tt.Error(err)\n\t}\n\t_, err = NewClient(context.Background(), WithEndpoint(\"127.0.0.1:9999\"), WithTLSConfig(&tls.Config{ServerName: \"www.kratos.com\", RootCAs: nil}))\n\tif err != nil {\n\t\tt.Error(err)\n\t}\n\t_, err = NewClient(context.Background(), WithDiscovery(&mockDiscovery{}), WithEndpoint(\"discovery:///go-kratos\"))\n\tif err != nil {\n\t\tt.Error(err)\n\t}\n\t_, err = NewClient(context.Background(), WithDiscovery(&mockDiscovery{}), WithEndpoint(\"127.0.0.1:8888\"))\n\tif err != nil {\n\t\tt.Error(err)\n\t}\n\t_, err = NewClient(context.Background(), WithEndpoint(\"127.0.0.1:8888:xxxxa\"))\n\tif err == nil {\n\t\tt.Error(\"except a parseTarget error\")\n\t}\n\t_, err = NewClient(context.Background(), WithDiscovery(&mockDiscovery{}), WithEndpoint(\"https://go-kratos.dev/\"))\n\tif err == nil {\n\t\tt.Error(\"err should not be equal to nil\")\n\t}\n\n\tclient, err := NewClient(\n\t\tcontext.Background(),\n\t\tWithDiscovery(&mockDiscovery{}),\n\t\tWithEndpoint(\"discovery:///go-kratos\"),\n\t\tWithMiddleware(func(handler middleware.Handler) middleware.Handler {\n\t\t\tt.Logf(\"handle in middleware\")\n\t\t\treturn func(ctx context.Context, req any) (any, error) {\n\t\t\t\treturn handler(ctx, req)\n\t\t\t}\n\t\t}),\n\t)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\terr = client.Invoke(context.Background(), http.MethodPost, \"/go\", map[string]string{\"name\": \"kratos\"}, nil, EmptyCallOption{}, &mockCallOption{})\n\tif err == nil {\n\t\tt.Error(\"err should not be equal to nil\")\n\t}\n\terr = client.Invoke(context.Background(), http.MethodPost, \"/go\", map[string]string{\"name\": \"kratos\"}, nil, EmptyCallOption{}, &mockCallOption{needErr: true})\n\tif err == nil {\n\t\tt.Error(\"err should be equal to callOption err\")\n\t}\n\tclient.opts.encoder = func(context.Context, string, any) (body []byte, err error) {\n\t\treturn nil, errors.New(\"mock test encoder error\")\n\t}\n\terr = client.Invoke(context.Background(), http.MethodPost, \"/go\", map[string]string{\"name\": \"kratos\"}, nil, EmptyCallOption{})\n\tif err == nil {\n\t\tt.Error(\"err should be equal to encoder error\")\n\t}\n}\n"
  },
  {
    "path": "transport/http/codec.go",
    "content": "package http\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"net/url\"\n\n\t\"github.com/gorilla/mux\"\n\n\t\"github.com/go-kratos/kratos/v2/encoding\"\n\t\"github.com/go-kratos/kratos/v2/errors\"\n\t\"github.com/go-kratos/kratos/v2/internal/httputil\"\n\t\"github.com/go-kratos/kratos/v2/transport/http/binding\"\n)\n\n// SupportPackageIsVersion1 These constants should not be referenced from any other code.\nconst SupportPackageIsVersion1 = true\n\n// Redirector replies to the request with a redirect to url\n// which may be a path relative to the request path.\ntype Redirector interface {\n\terror\n\tRedirect() (string, int)\n}\n\n// Request type net/http.\ntype Request = http.Request\n\n// ResponseWriter type net/http.\ntype ResponseWriter = http.ResponseWriter\n\n// Flusher type net/http\ntype Flusher = http.Flusher\n\n// DecodeRequestFunc is decode request func.\ntype DecodeRequestFunc func(*http.Request, any) error\n\n// EncodeResponseFunc is encode response func.\ntype EncodeResponseFunc func(http.ResponseWriter, *http.Request, any) error\n\n// EncodeErrorFunc is encode error func.\ntype EncodeErrorFunc func(http.ResponseWriter, *http.Request, error)\n\n// DefaultRequestVars decodes the request vars to object.\nfunc DefaultRequestVars(r *http.Request, v any) error {\n\traws := mux.Vars(r)\n\tvars := make(url.Values, len(raws))\n\tfor k, v := range raws {\n\t\tvars[k] = []string{v}\n\t}\n\treturn binding.BindQuery(vars, v)\n}\n\n// DefaultRequestQuery decodes the request vars to object.\nfunc DefaultRequestQuery(r *http.Request, v any) error {\n\treturn binding.BindQuery(r.URL.Query(), v)\n}\n\n// DefaultRequestDecoder decodes the request body to object.\nfunc DefaultRequestDecoder(r *http.Request, v any) error {\n\tcodec, ok := CodecForRequest(r, \"Content-Type\")\n\tif !ok {\n\t\treturn errors.BadRequest(\"CODEC\", fmt.Sprintf(\"unregister Content-Type: %s\", r.Header.Get(\"Content-Type\")))\n\t}\n\tdata, err := io.ReadAll(r.Body)\n\n\t// reset body.\n\tr.Body = io.NopCloser(bytes.NewBuffer(data))\n\n\tif err != nil {\n\t\treturn errors.BadRequest(\"CODEC\", err.Error())\n\t}\n\tif len(data) == 0 {\n\t\treturn nil\n\t}\n\tif err = codec.Unmarshal(data, v); err != nil {\n\t\treturn errors.BadRequest(\"CODEC\", fmt.Sprintf(\"body unmarshal %s\", err.Error()))\n\t}\n\treturn nil\n}\n\n// DefaultResponseEncoder encodes the object to the HTTP response.\nfunc DefaultResponseEncoder(w http.ResponseWriter, r *http.Request, v any) error {\n\tif v == nil {\n\t\treturn nil\n\t}\n\tif rd, ok := v.(Redirector); ok {\n\t\turl, code := rd.Redirect()\n\t\thttp.Redirect(w, r, url, code)\n\t\treturn nil\n\t}\n\tcodec, _ := CodecForRequest(r, \"Accept\")\n\tdata, err := codec.Marshal(v)\n\tif err != nil {\n\t\treturn err\n\t}\n\tw.Header().Set(\"Content-Type\", httputil.ContentType(codec.Name()))\n\t_, err = w.Write(data)\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\n// DefaultErrorEncoder encodes the error to the HTTP response.\nfunc DefaultErrorEncoder(w http.ResponseWriter, r *http.Request, err error) {\n\tvar rd *redirect\n\tif errors.As(err, &rd) {\n\t\turl, code := rd.Redirect()\n\t\thttp.Redirect(w, r, url, code)\n\t\treturn\n\t}\n\tse := errors.FromError(err)\n\tcodec, _ := CodecForRequest(r, \"Accept\")\n\tbody, err := codec.Marshal(se)\n\tif err != nil {\n\t\tw.WriteHeader(http.StatusInternalServerError)\n\t\treturn\n\t}\n\tw.Header().Set(\"Content-Type\", httputil.ContentType(codec.Name()))\n\tw.WriteHeader(int(se.Code))\n\t_, _ = w.Write(body)\n}\n\n// CodecForRequest get encoding.Codec via http.Request\nfunc CodecForRequest(r *http.Request, name string) (encoding.Codec, bool) {\n\tfor _, accept := range r.Header[name] {\n\t\tcodec := encoding.GetCodec(httputil.ContentSubtype(accept))\n\t\tif codec != nil {\n\t\t\treturn codec, true\n\t\t}\n\t}\n\treturn encoding.GetCodec(\"json\"), false\n}\n"
  },
  {
    "path": "transport/http/codec_go1.20.go",
    "content": "//go:build go1.20\n\npackage http\n\nimport \"net/http\"\n\n// ResponseController is type net/http.ResponseController which was added in Go 1.20.\n\ntype ResponseController = http.ResponseController\n"
  },
  {
    "path": "transport/http/codec_test.go",
    "content": "package http\n\nimport (\n\t\"bytes\"\n\t\"io\"\n\t\"net/http\"\n\t\"testing\"\n\n\t\"github.com/go-kratos/kratos/v2/encoding\"\n\t\"github.com/go-kratos/kratos/v2/errors\"\n)\n\nfunc TestDefaultRequestDecoder(t *testing.T) {\n\tvar (\n\t\tbodyStr = `{\"a\":\"1\", \"b\": 2}`\n\t\tr, _    = http.NewRequest(http.MethodPost, \"\", io.NopCloser(bytes.NewBufferString(bodyStr)))\n\t)\n\tr.Header.Set(\"Content-Type\", \"application/json\")\n\n\tv1 := &struct {\n\t\tA string `json:\"a\"`\n\t\tB int64  `json:\"b\"`\n\t}{}\n\terr := DefaultRequestDecoder(r, &v1)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif v1.A != \"1\" {\n\t\tt.Errorf(\"expected %v, got %v\", \"1\", v1.A)\n\t}\n\tif v1.B != int64(2) {\n\t\tt.Errorf(\"expected %v, got %v\", 2, v1.B)\n\t}\n\n\tdata, err := io.ReadAll(r.Body)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif bodyStr != string(data) {\n\t\tt.Errorf(\"expected %v, got %v\", bodyStr, string(data))\n\t}\n}\n\ntype mockResponseWriter struct {\n\tStatusCode int\n\tData       []byte\n\theader     http.Header\n}\n\nfunc (w *mockResponseWriter) Header() http.Header {\n\treturn w.header\n}\n\nfunc (w *mockResponseWriter) Write(b []byte) (int, error) {\n\tw.Data = b\n\treturn len(b), nil\n}\n\nfunc (w *mockResponseWriter) WriteHeader(statusCode int) {\n\tw.StatusCode = statusCode\n}\n\ntype errorCodec struct{}\n\nfunc (errorCodec) Marshal(any) ([]byte, error) {\n\treturn nil, errors.New(500, \"mock\", \"marshal error\")\n}\n\nfunc (errorCodec) Unmarshal([]byte, any) error {\n\treturn nil\n}\n\nfunc (errorCodec) Name() string {\n\treturn \"mock\"\n}\n\nfunc TestDefaultResponseEncoder(t *testing.T) {\n\tvar (\n\t\tw    = &mockResponseWriter{StatusCode: 200, header: make(http.Header)}\n\t\tr, _ = http.NewRequest(http.MethodPost, \"\", nil)\n\t\tv    = &struct {\n\t\t\tA string `json:\"a\"`\n\t\t\tB int64  `json:\"b\"`\n\t\t}{\n\t\t\tA: \"1\",\n\t\t\tB: 2,\n\t\t}\n\t)\n\tr.Header.Set(\"Content-Type\", \"application/json\")\n\n\terr := DefaultResponseEncoder(w, r, v)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif w.Header().Get(\"Content-Type\") != \"application/json\" {\n\t\tt.Errorf(\"expected %v, got %v\", \"application/json\", w.Header().Get(\"Content-Type\"))\n\t}\n\tif w.StatusCode != 200 {\n\t\tt.Errorf(\"expected %v, got %v\", 200, w.StatusCode)\n\t}\n\tif w.Data == nil {\n\t\tt.Errorf(\"expected not nil, got %v\", w.Data)\n\t}\n}\n\nfunc TestDefaultErrorEncoder(t *testing.T) {\n\tvar (\n\t\tw    = &mockResponseWriter{header: make(http.Header)}\n\t\tr, _ = http.NewRequest(http.MethodPost, \"\", nil)\n\t\terr  = errors.New(511, \"\", \"\")\n\t)\n\tr.Header.Set(\"Content-Type\", \"application/json\")\n\n\tDefaultErrorEncoder(w, r, err)\n\tif w.Header().Get(\"Content-Type\") != \"application/json\" {\n\t\tt.Errorf(\"expected %v, got %v\", \"application/json\", w.Header().Get(\"Content-Type\"))\n\t}\n\tif w.StatusCode != 511 {\n\t\tt.Errorf(\"expected %v, got %v\", 511, w.StatusCode)\n\t}\n\tif w.Data == nil {\n\t\tt.Errorf(\"expected not nil, got %v\", w.Data)\n\t}\n}\n\nfunc TestDefaultErrorEncoderRedirect(t *testing.T) {\n\tw := &mockResponseWriter{header: make(http.Header)}\n\tr, _ := http.NewRequest(http.MethodGet, \"/test\", nil)\n\n\tDefaultErrorEncoder(w, r, NewRedirect(\"/redirect\", http.StatusTemporaryRedirect))\n\n\tif w.StatusCode != http.StatusTemporaryRedirect {\n\t\tt.Errorf(\"expected %v, got %v\", http.StatusTemporaryRedirect, w.StatusCode)\n\t}\n\tif w.Header().Get(\"Location\") != \"/redirect\" {\n\t\tt.Errorf(\"expected %v, got %v\", \"/redirect\", w.Header().Get(\"Location\"))\n\t}\n}\n\nfunc TestDefaultErrorEncoderMarshalError(t *testing.T) {\n\tencoding.RegisterCodec(errorCodec{})\n\tw := &mockResponseWriter{header: make(http.Header)}\n\tr, _ := http.NewRequest(http.MethodGet, \"\", nil)\n\tr.Header.Set(\"Accept\", \"application/mock\")\n\n\tDefaultErrorEncoder(w, r, errors.New(500, \"mock\", \"marshal error\"))\n\n\tif w.StatusCode != http.StatusInternalServerError {\n\t\tt.Errorf(\"expected %v, got %v\", http.StatusInternalServerError, w.StatusCode)\n\t}\n\tif w.Header().Get(\"Content-Type\") != \"\" {\n\t\tt.Errorf(\"expected empty content type, got %v\", w.Header().Get(\"Content-Type\"))\n\t}\n\tif w.Data != nil {\n\t\tt.Errorf(\"expected nil, got %v\", w.Data)\n\t}\n}\n\nfunc TestDefaultResponseEncoderEncodeNil(t *testing.T) {\n\tvar (\n\t\tw    = &mockResponseWriter{StatusCode: 204, header: make(http.Header)}\n\t\tr, _ = http.NewRequest(http.MethodPost, \"\", io.NopCloser(bytes.NewBufferString(\"<xml></xml>\")))\n\t)\n\tr.Header.Set(\"Content-Type\", \"application/json\")\n\n\terr := DefaultResponseEncoder(w, r, nil)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif w.Header().Get(\"Content-Type\") != \"\" {\n\t\tt.Errorf(\"expected empty string, got %v\", w.Header().Get(\"Content-Type\"))\n\t}\n\tif w.StatusCode != 204 {\n\t\tt.Errorf(\"expected %v, got %v\", 204, w.StatusCode)\n\t}\n\tif w.Data != nil {\n\t\tt.Errorf(\"expected nil, got %v\", w.Data)\n\t}\n}\n\nfunc TestCodecForRequest(t *testing.T) {\n\tr, _ := http.NewRequest(http.MethodPost, \"\", io.NopCloser(bytes.NewBufferString(\"<xml></xml>\")))\n\tr.Header.Set(\"Content-Type\", \"application/xml\")\n\tc, ok := CodecForRequest(r, \"Content-Type\")\n\tif !ok {\n\t\tt.Fatalf(\"expected true, got %v\", ok)\n\t}\n\tif c.Name() != \"xml\" {\n\t\tt.Errorf(\"expected %v, got %v\", \"xml\", c.Name())\n\t}\n\n\tr, _ = http.NewRequest(http.MethodPost, \"\", io.NopCloser(bytes.NewBufferString(`{\"a\":\"1\", \"b\": 2}`)))\n\tr.Header.Set(\"Content-Type\", \"blablablabla\")\n\tc, ok = CodecForRequest(r, \"Content-Type\")\n\tif ok {\n\t\tt.Fatalf(\"expected false, got %v\", ok)\n\t}\n\tif c.Name() != \"json\" {\n\t\tt.Errorf(\"expected %v, got %v\", \"json\", c.Name())\n\t}\n}\n"
  },
  {
    "path": "transport/http/context.go",
    "content": "package http\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"encoding/xml\"\n\t\"io\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"time\"\n\n\t\"github.com/gorilla/mux\"\n\n\t\"github.com/go-kratos/kratos/v2/middleware\"\n\t\"github.com/go-kratos/kratos/v2/transport\"\n\t\"github.com/go-kratos/kratos/v2/transport/http/binding\"\n)\n\nvar _ Context = (*wrapper)(nil)\n\n// Context is an HTTP Context.\ntype Context interface {\n\tcontext.Context\n\tVars() url.Values\n\tQuery() url.Values\n\tForm() url.Values\n\tHeader() http.Header\n\tRequest() *http.Request\n\tResponse() http.ResponseWriter\n\tMiddleware(middleware.Handler) middleware.Handler\n\tBind(any) error\n\tBindVars(any) error\n\tBindQuery(any) error\n\tBindForm(any) error\n\tReturns(any, error) error\n\tResult(int, any) error\n\tJSON(int, any) error\n\tXML(int, any) error\n\tString(int, string) error\n\tBlob(int, string, []byte) error\n\tStream(int, string, io.Reader) error\n\tReset(http.ResponseWriter, *http.Request)\n}\n\ntype responseWriter struct {\n\tcode int\n\tw    http.ResponseWriter\n}\n\nfunc (w *responseWriter) reset(res http.ResponseWriter) {\n\tw.w = res\n\tw.code = http.StatusOK\n}\nfunc (w *responseWriter) Header() http.Header        { return w.w.Header() }\nfunc (w *responseWriter) WriteHeader(statusCode int) { w.code = statusCode }\nfunc (w *responseWriter) Write(data []byte) (int, error) {\n\tw.w.WriteHeader(w.code)\n\treturn w.w.Write(data)\n}\nfunc (w *responseWriter) Unwrap() http.ResponseWriter { return w.w }\n\ntype wrapper struct {\n\trouter *Router\n\treq    *http.Request\n\tres    http.ResponseWriter\n\tw      responseWriter\n}\n\nfunc (c *wrapper) Header() http.Header {\n\treturn c.req.Header\n}\n\nfunc (c *wrapper) Vars() url.Values {\n\traws := mux.Vars(c.req)\n\tvars := make(url.Values, len(raws))\n\tfor k, v := range raws {\n\t\tvars[k] = []string{v}\n\t}\n\treturn vars\n}\n\nfunc (c *wrapper) Form() url.Values {\n\tif err := c.req.ParseForm(); err != nil {\n\t\treturn url.Values{}\n\t}\n\treturn c.req.Form\n}\n\nfunc (c *wrapper) Query() url.Values {\n\treturn c.req.URL.Query()\n}\nfunc (c *wrapper) Request() *http.Request        { return c.req }\nfunc (c *wrapper) Response() http.ResponseWriter { return c.res }\nfunc (c *wrapper) Middleware(h middleware.Handler) middleware.Handler {\n\tif tr, ok := transport.FromServerContext(c.req.Context()); ok {\n\t\treturn middleware.Chain(c.router.srv.middleware.Match(tr.Operation())...)(h)\n\t}\n\treturn middleware.Chain(c.router.srv.middleware.Match(c.req.URL.Path)...)(h)\n}\nfunc (c *wrapper) Bind(v any) error      { return c.router.srv.decBody(c.req, v) }\nfunc (c *wrapper) BindVars(v any) error  { return c.router.srv.decVars(c.req, v) }\nfunc (c *wrapper) BindQuery(v any) error { return c.router.srv.decQuery(c.req, v) }\nfunc (c *wrapper) BindForm(v any) error  { return binding.BindForm(c.req, v) }\nfunc (c *wrapper) Returns(v any, err error) error {\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn c.router.srv.enc(&c.w, c.req, v)\n}\n\nfunc (c *wrapper) Result(code int, v any) error {\n\tc.w.WriteHeader(code)\n\treturn c.router.srv.enc(&c.w, c.req, v)\n}\n\nfunc (c *wrapper) JSON(code int, v any) error {\n\tc.res.Header().Set(\"Content-Type\", \"application/json\")\n\tc.res.WriteHeader(code)\n\treturn json.NewEncoder(c.res).Encode(v)\n}\n\nfunc (c *wrapper) XML(code int, v any) error {\n\tc.res.Header().Set(\"Content-Type\", \"application/xml\")\n\tc.res.WriteHeader(code)\n\treturn xml.NewEncoder(c.res).Encode(v)\n}\n\nfunc (c *wrapper) String(code int, text string) error {\n\tc.res.Header().Set(\"Content-Type\", \"text/plain\")\n\tc.res.WriteHeader(code)\n\t_, err := c.res.Write([]byte(text))\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc (c *wrapper) Blob(code int, contentType string, data []byte) error {\n\tc.res.Header().Set(\"Content-Type\", contentType)\n\tc.res.WriteHeader(code)\n\t_, err := c.res.Write(data)\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc (c *wrapper) Stream(code int, contentType string, rd io.Reader) error {\n\tc.res.Header().Set(\"Content-Type\", contentType)\n\tc.res.WriteHeader(code)\n\t_, err := io.Copy(c.res, rd)\n\treturn err\n}\n\nfunc (c *wrapper) Reset(res http.ResponseWriter, req *http.Request) {\n\tc.w.reset(res)\n\tc.res = res\n\tc.req = req\n}\n\nfunc (c *wrapper) Deadline() (time.Time, bool) {\n\tif c.req == nil {\n\t\treturn time.Time{}, false\n\t}\n\treturn c.req.Context().Deadline()\n}\n\nfunc (c *wrapper) Done() <-chan struct{} {\n\tif c.req == nil {\n\t\treturn nil\n\t}\n\treturn c.req.Context().Done()\n}\n\nfunc (c *wrapper) Err() error {\n\tif c.req == nil {\n\t\treturn context.Canceled\n\t}\n\treturn c.req.Context().Err()\n}\n\nfunc (c *wrapper) Value(key any) any {\n\tif c.req == nil {\n\t\treturn nil\n\t}\n\treturn c.req.Context().Value(key)\n}\n"
  },
  {
    "path": "transport/http/context_test.go",
    "content": "package http\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"errors\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"net/url\"\n\t\"reflect\"\n\t\"testing\"\n\t\"time\"\n)\n\nvar testRouter = &Router{srv: NewServer()}\n\nfunc TestContextHeader(t *testing.T) {\n\tw := wrapper{\n\t\trouter: testRouter,\n\t\treq:    &http.Request{Header: map[string][]string{\"name\": {\"kratos\"}}},\n\t\tres:    nil,\n\t\tw:      responseWriter{},\n\t}\n\th := w.Header()\n\tif !reflect.DeepEqual(h, http.Header{\"name\": {\"kratos\"}}) {\n\t\tt.Errorf(\"expected %v, got %v\", http.Header{\"name\": {\"kratos\"}}, h)\n\t}\n}\n\nfunc TestContextForm(t *testing.T) {\n\tw := wrapper{\n\t\trouter: testRouter,\n\t\treq:    &http.Request{Header: map[string][]string{\"name\": {\"kratos\"}}, Method: http.MethodPost},\n\t\tres:    nil,\n\t\tw:      responseWriter{},\n\t}\n\tform := w.Form()\n\tif !reflect.DeepEqual(form, url.Values{}) {\n\t\tt.Errorf(\"expected %v, got %v\", url.Values{}, form)\n\t}\n\n\tw = wrapper{\n\t\trouter: testRouter,\n\t\treq:    &http.Request{Form: map[string][]string{\"name\": {\"kratos\"}}},\n\t\tres:    nil,\n\t\tw:      responseWriter{},\n\t}\n\tform = w.Form()\n\tif !reflect.DeepEqual(form, url.Values{\"name\": {\"kratos\"}}) {\n\t\tt.Errorf(\"expected %v, got %v\", url.Values{\"name\": {\"kratos\"}}, form)\n\t}\n}\n\nfunc TestContextQuery(t *testing.T) {\n\tw := wrapper{\n\t\trouter: testRouter,\n\t\treq:    &http.Request{URL: &url.URL{Scheme: \"https\", Host: \"github.com\", Path: \"go-kratos/kratos\", RawQuery: \"page=1\"}, Method: http.MethodPost},\n\t\tres:    nil,\n\t\tw:      responseWriter{},\n\t}\n\tq := w.Query()\n\tif !reflect.DeepEqual(q, url.Values{\"page\": {\"1\"}}) {\n\t\tt.Errorf(\"expected %v, got %v\", url.Values{\"page\": {\"1\"}}, q)\n\t}\n}\n\nfunc TestContextRequest(t *testing.T) {\n\treq := &http.Request{Method: http.MethodPost}\n\tw := wrapper{\n\t\trouter: testRouter,\n\t\treq:    req,\n\t\tres:    nil,\n\t\tw:      responseWriter{},\n\t}\n\tres := w.Request()\n\tif !reflect.DeepEqual(res, req) {\n\t\tt.Errorf(\"expected %v, got %v\", req, res)\n\t}\n}\n\nfunc TestContextResponse(t *testing.T) {\n\tres := httptest.NewRecorder()\n\tw := wrapper{\n\t\trouter: &Router{srv: &Server{enc: DefaultResponseEncoder}},\n\t\treq:    &http.Request{Method: http.MethodPost},\n\t\tres:    res,\n\t\tw:      responseWriter{200, res},\n\t}\n\tif !reflect.DeepEqual(w.Response(), res) {\n\t\tt.Errorf(\"expected %v, got %v\", res, w.Response())\n\t}\n\terr := w.Returns(map[string]string{}, nil)\n\tif err != nil {\n\t\tt.Errorf(\"expected %v, got %v\", nil, err)\n\t}\n\tneedErr := errors.New(\"some error\")\n\terr = w.Returns(map[string]string{}, needErr)\n\tif !errors.Is(err, needErr) {\n\t\tt.Errorf(\"expected %v, got %v\", needErr, err)\n\t}\n}\n\nfunc TestResponseUnwrap(t *testing.T) {\n\tres := httptest.NewRecorder()\n\tf := func(rw http.ResponseWriter, _ *http.Request, _ any) error {\n\t\tu, ok := rw.(interface {\n\t\t\tUnwrap() http.ResponseWriter\n\t\t})\n\t\tif !ok {\n\t\t\treturn errors.New(\"can not unwrap\")\n\t\t}\n\t\tw := u.Unwrap()\n\t\tif !reflect.DeepEqual(w, res) {\n\t\t\treturn errors.New(\"underlying response writer not equal\")\n\t\t}\n\t\treturn nil\n\t}\n\n\tw := wrapper{\n\t\trouter: &Router{srv: &Server{enc: f}},\n\t\treq:    nil,\n\t\tres:    res,\n\t\tw:      responseWriter{200, res},\n\t}\n\terr := w.Result(200, \"ok\")\n\tif err != nil {\n\t\tt.Errorf(\"expected %v, got %v\", nil, err)\n\t}\n}\n\nfunc TestContextBindQuery(t *testing.T) {\n\tw := wrapper{\n\t\trouter: testRouter,\n\t\treq:    &http.Request{URL: &url.URL{Scheme: \"https\", Host: \"go-kratos-dev\", RawQuery: \"page=2\"}},\n\t\tres:    nil,\n\t\tw:      responseWriter{},\n\t}\n\ttype BindQuery struct {\n\t\tPage int `json:\"page\"`\n\t}\n\tb := BindQuery{}\n\terr := w.BindQuery(&b)\n\tif err != nil {\n\t\tt.Errorf(\"expected %v, got %v\", nil, err)\n\t}\n\tif !reflect.DeepEqual(b, BindQuery{Page: 2}) {\n\t\tt.Errorf(\"expected %v, got %v\", BindQuery{Page: 2}, b)\n\t}\n}\n\nfunc TestContextBindForm(t *testing.T) {\n\tw := wrapper{\n\t\trouter: testRouter,\n\t\treq:    &http.Request{URL: &url.URL{Scheme: \"https\", Host: \"go-kratos-dev\"}, Form: map[string][]string{\"page\": {\"2\"}}},\n\t\tres:    nil,\n\t\tw:      responseWriter{},\n\t}\n\ttype BindForm struct {\n\t\tPage int `json:\"page\"`\n\t}\n\tb := BindForm{}\n\terr := w.BindForm(&b)\n\tif err != nil {\n\t\tt.Errorf(\"expected %v, got %v\", nil, err)\n\t}\n\tif !reflect.DeepEqual(b, BindForm{Page: 2}) {\n\t\tt.Errorf(\"expected %v, got %v\", BindForm{Page: 2}, b)\n\t}\n}\n\nfunc TestContextResponseReturn(t *testing.T) {\n\twriter := httptest.NewRecorder()\n\tw := wrapper{\n\t\trouter: testRouter,\n\t\treq:    nil,\n\t\tres:    writer,\n\t\tw:      responseWriter{},\n\t}\n\terr := w.JSON(200, \"success\")\n\tif err != nil {\n\t\tt.Errorf(\"expected %v, got %v\", nil, err)\n\t}\n\terr = w.XML(200, \"success\")\n\tif err != nil {\n\t\tt.Errorf(\"expected %v, got %v\", nil, err)\n\t}\n\terr = w.String(200, \"success\")\n\tif err != nil {\n\t\tt.Errorf(\"expected %v, got %v\", nil, err)\n\t}\n\terr = w.Blob(200, \"blob\", []byte(\"success\"))\n\tif err != nil {\n\t\tt.Errorf(\"expected %v, got %v\", nil, err)\n\t}\n\terr = w.Stream(200, \"stream\", bytes.NewBuffer([]byte(\"success\")))\n\tif err != nil {\n\t\tt.Errorf(\"expected %v, got %v\", nil, err)\n\t}\n}\n\nfunc TestContextCtx(t *testing.T) {\n\tctx, cancel := context.WithTimeout(context.Background(), time.Second)\n\tdefer cancel()\n\treq := &http.Request{Method: http.MethodPost}\n\treq = req.WithContext(ctx)\n\tw := wrapper{\n\t\trouter: testRouter,\n\t\treq:    req,\n\t\tres:    nil,\n\t\tw:      responseWriter{},\n\t}\n\t_, ok := w.Deadline()\n\tif !ok {\n\t\tt.Errorf(\"expected %v, got %v\", true, ok)\n\t}\n\tdone := w.Done()\n\tif done == nil {\n\t\tt.Errorf(\"expected %v, got %v\", true, ok)\n\t}\n\terr := w.Err()\n\tif err != nil {\n\t\tt.Errorf(\"expected %v, got %v\", nil, err)\n\t}\n\tv := w.Value(\"test\")\n\tif v != nil {\n\t\tt.Errorf(\"expected %v, got %v\", nil, v)\n\t}\n\n\tw = wrapper{\n\t\trouter: &Router{srv: &Server{enc: DefaultResponseEncoder}},\n\t\treq:    nil,\n\t\tres:    nil,\n\t\tw:      responseWriter{},\n\t}\n\t_, ok = w.Deadline()\n\tif ok {\n\t\tt.Errorf(\"expected %v, got %v\", false, ok)\n\t}\n\tdone = w.Done()\n\tif done != nil {\n\t\tt.Errorf(\"expected not nil, got %v\", done)\n\t}\n\terr = w.Err()\n\tif err == nil {\n\t\tt.Errorf(\"expected not %v, got %v\", nil, err)\n\t}\n\tv = w.Value(\"test\")\n\tif v != nil {\n\t\tt.Errorf(\"expected %v, got %v\", nil, v)\n\t}\n}\n"
  },
  {
    "path": "transport/http/filter.go",
    "content": "package http\n\nimport \"net/http\"\n\n// FilterFunc is a function which receives a http.Handler and returns another http.Handler.\ntype FilterFunc func(http.Handler) http.Handler\n\n// FilterChain returns a FilterFunc that specifies the chained handler for HTTP Router.\nfunc FilterChain(filters ...FilterFunc) FilterFunc {\n\treturn func(next http.Handler) http.Handler {\n\t\tfor i := len(filters) - 1; i >= 0; i-- {\n\t\t\tnext = filters[i](next)\n\t\t}\n\t\treturn next\n\t}\n}\n"
  },
  {
    "path": "transport/http/pprof/pprof.go",
    "content": "package pprof\n\nimport (\n\t\"net/http\"\n\t\"net/http/pprof\"\n)\n\n// NewHandler new a pprof handler.\nfunc NewHandler() http.Handler {\n\tmux := http.NewServeMux()\n\tmux.HandleFunc(\"/debug/pprof/\", pprof.Index)\n\tmux.HandleFunc(\"/debug/pprof/cmdline\", pprof.Cmdline)\n\tmux.HandleFunc(\"/debug/pprof/profile\", pprof.Profile)\n\tmux.HandleFunc(\"/debug/pprof/symbol\", pprof.Symbol)\n\tmux.HandleFunc(\"/debug/pprof/trace\", pprof.Trace)\n\treturn mux\n}\n"
  },
  {
    "path": "transport/http/redirect.go",
    "content": "package http\n\ntype redirect struct {\n\tURL  string\n\tCode int\n}\n\nfunc (r *redirect) Redirect() (string, int) {\n\treturn r.URL, r.Code\n}\n\nfunc (r *redirect) Error() string {\n\treturn \"redirect to \" + r.URL\n}\n\n// NewRedirect new a redirect with url, which may be a path relative to the request path.\n// The provided code should be in the 3xx range and is usually StatusMovedPermanently, StatusFound or StatusSeeOther.\n// If the Content-Type header has not been set, Redirect sets it to \"text/html; charset=utf-8\" and writes a small HTML body.\n// Setting the Content-Type header to any value, including nil, disables that behavior.\nfunc NewRedirect(url string, code int) Redirector {\n\treturn &redirect{URL: url, Code: code}\n}\n"
  },
  {
    "path": "transport/http/redirect_test.go",
    "content": "package http\n\nimport (\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"testing\"\n)\n\nfunc TestRedirect(t *testing.T) {\n\tvar (\n\t\tredirectURL  = \"/redirect\"\n\t\tredirectCode = 302\n\t)\n\tr := httptest.NewRequest(http.MethodPost, \"/test\", nil)\n\tw := httptest.NewRecorder()\n\t_ = DefaultResponseEncoder(w, r, NewRedirect(redirectURL, redirectCode))\n\n\tif w.Code != redirectCode {\n\t\tt.Fatalf(\"want %d but got %d\", redirectCode, w.Code)\n\t}\n\tif v := w.Header().Get(\"Location\"); v != redirectURL {\n\t\tt.Fatalf(\"want %s but got %s\", redirectURL, v)\n\t}\n}\n"
  },
  {
    "path": "transport/http/resolver.go",
    "content": "package http\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"net/url\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/google/uuid\"\n\n\t\"github.com/go-kratos/aegis/subset\"\n\t\"github.com/go-kratos/kratos/v2/internal/endpoint\"\n\t\"github.com/go-kratos/kratos/v2/log\"\n\t\"github.com/go-kratos/kratos/v2/registry\"\n\t\"github.com/go-kratos/kratos/v2/selector\"\n)\n\n// Target is resolver target\ntype Target struct {\n\tScheme    string\n\tAuthority string\n\tEndpoint  string\n}\n\nfunc parseTarget(endpoint string, insecure bool) (*Target, error) {\n\tif !strings.Contains(endpoint, \"://\") {\n\t\tif insecure {\n\t\t\tendpoint = \"http://\" + endpoint\n\t\t} else {\n\t\t\tendpoint = \"https://\" + endpoint\n\t\t}\n\t}\n\tu, err := url.Parse(endpoint)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\ttarget := &Target{Scheme: u.Scheme, Authority: u.Host}\n\tif len(u.Path) > 1 {\n\t\ttarget.Endpoint = u.Path[1:]\n\t}\n\treturn target, nil\n}\n\ntype resolver struct {\n\trebalancer selector.Rebalancer\n\n\ttarget      *Target\n\twatcher     registry.Watcher\n\tselectorKey string\n\tsubsetSize  int\n\n\tinsecure bool\n}\n\nfunc newResolver(ctx context.Context, discovery registry.Discovery, target *Target,\n\trebalancer selector.Rebalancer, block, insecure bool, subsetSize int,\n) (*resolver, error) {\n\t// this is new resolver\n\twatcher, err := discovery.Watch(ctx, target.Endpoint)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tr := &resolver{\n\t\ttarget:      target,\n\t\twatcher:     watcher,\n\t\trebalancer:  rebalancer,\n\t\tinsecure:    insecure,\n\t\tselectorKey: uuid.New().String(),\n\t\tsubsetSize:  subsetSize,\n\t}\n\tif block {\n\t\tdone := make(chan error, 1)\n\t\tgo func() {\n\t\t\tfor {\n\t\t\t\tservices, err := watcher.Next()\n\t\t\t\tif err != nil {\n\t\t\t\t\tdone <- err\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tif r.update(services) {\n\t\t\t\t\tdone <- nil\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t}\n\t\t}()\n\t\tselect {\n\t\tcase err := <-done:\n\t\t\tif err != nil {\n\t\t\t\tstopErr := watcher.Stop()\n\t\t\t\tif stopErr != nil {\n\t\t\t\t\tlog.Errorf(\"failed to http client watch stop: %v, error: %+v\", target, stopErr)\n\t\t\t\t}\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\tcase <-ctx.Done():\n\t\t\tlog.Errorf(\"http client watch service %v reaching context deadline!\", target)\n\t\t\tstopErr := watcher.Stop()\n\t\t\tif stopErr != nil {\n\t\t\t\tlog.Errorf(\"failed to http client watch stop: %v, error: %+v\", target, stopErr)\n\t\t\t}\n\t\t\treturn nil, ctx.Err()\n\t\t}\n\t}\n\tgo func() {\n\t\tfor {\n\t\t\tservices, err := watcher.Next()\n\t\t\tif err != nil {\n\t\t\t\tif errors.Is(err, context.Canceled) {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tlog.Errorf(\"http client watch service %v got unexpected error:=%v\", target, err)\n\t\t\t\ttime.Sleep(time.Second)\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tr.update(services)\n\t\t}\n\t}()\n\treturn r, nil\n}\n\nfunc (r *resolver) update(services []*registry.ServiceInstance) bool {\n\tfiltered := make([]*registry.ServiceInstance, 0, len(services))\n\tfor _, ins := range services {\n\t\tept, err := endpoint.ParseEndpoint(ins.Endpoints, endpoint.Scheme(\"http\", !r.insecure))\n\t\tif err != nil {\n\t\t\tlog.Errorf(\"Failed to parse (%v) discovery endpoint: %v error %v\", r.target, ins.Endpoints, err)\n\t\t\tcontinue\n\t\t}\n\t\tif ept == \"\" {\n\t\t\tcontinue\n\t\t}\n\t\tfiltered = append(filtered, ins)\n\t}\n\tif r.subsetSize != 0 {\n\t\tfiltered = subset.Subset(r.selectorKey, filtered, r.subsetSize)\n\t}\n\tnodes := make([]selector.Node, 0, len(filtered))\n\tfor _, ins := range filtered {\n\t\tept, _ := endpoint.ParseEndpoint(ins.Endpoints, endpoint.Scheme(\"http\", !r.insecure))\n\t\tnodes = append(nodes, selector.NewNode(\"http\", ept, ins))\n\t}\n\n\tif len(nodes) == 0 {\n\t\tlog.Warnf(\"[http resolver]Zero endpoint found,refused to write,set: %s ins: %v\", r.target.Endpoint, nodes)\n\t\treturn false\n\t}\n\tr.rebalancer.Apply(nodes)\n\treturn true\n}\n\nfunc (r *resolver) Close() error {\n\treturn r.watcher.Stop()\n}\n"
  },
  {
    "path": "transport/http/resolver_test.go",
    "content": "package http\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"reflect\"\n\t\"strconv\"\n\t\"sync\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/go-kratos/kratos/v2/registry\"\n\t\"github.com/go-kratos/kratos/v2/selector\"\n)\n\nfunc TestParseTarget(t *testing.T) {\n\ttarget, err := parseTarget(\"localhost:8000\", true)\n\tif err != nil {\n\t\tt.Errorf(\"expect %v, got %v\", nil, err)\n\t}\n\tif !reflect.DeepEqual(&Target{Scheme: \"http\", Authority: \"localhost:8000\"}, target) {\n\t\tt.Errorf(\"expect %v, got %v\", &Target{Scheme: \"http\", Authority: \"localhost:8000\"}, target)\n\t}\n\n\ttarget, err = parseTarget(\"discovery:///demo\", true)\n\tif err != nil {\n\t\tt.Errorf(\"expect %v, got %v\", nil, err)\n\t}\n\tif !reflect.DeepEqual(&Target{Scheme: \"discovery\", Authority: \"\", Endpoint: \"demo\"}, target) {\n\t\tt.Errorf(\"expect %v, got %v\", &Target{Scheme: \"discovery\", Authority: \"\", Endpoint: \"demo\"}, target)\n\t}\n\n\ttarget, err = parseTarget(\"127.0.0.1:8000\", true)\n\tif err != nil {\n\t\tt.Errorf(\"expect %v, got %v\", nil, err)\n\t}\n\tif !reflect.DeepEqual(&Target{Scheme: \"http\", Authority: \"127.0.0.1:8000\"}, target) {\n\t\tt.Errorf(\"expect %v, got %v\", &Target{Scheme: \"http\", Authority: \"127.0.0.1:8000\"}, target)\n\t}\n\n\ttarget, err = parseTarget(\"https://127.0.0.1:8000\", false)\n\tif err != nil {\n\t\tt.Errorf(\"expect %v, got %v\", nil, err)\n\t}\n\tif !reflect.DeepEqual(&Target{Scheme: \"https\", Authority: \"127.0.0.1:8000\"}, target) {\n\t\tt.Errorf(\"expect %v, got %v\", &Target{Scheme: \"https\", Authority: \"127.0.0.1:8000\"}, target)\n\t}\n\n\ttarget, err = parseTarget(\"127.0.0.1:8000\", false)\n\tif err != nil {\n\t\tt.Errorf(\"expect %v, got %v\", nil, err)\n\t}\n\tif !reflect.DeepEqual(&Target{Scheme: \"https\", Authority: \"127.0.0.1:8000\"}, target) {\n\t\tt.Errorf(\"expect %v, got %v\", &Target{Scheme: \"https\", Authority: \"127.0.0.1:8000\"}, target)\n\t}\n}\n\ntype mockRebalancer struct{}\n\nfunc (m *mockRebalancer) Apply(_ []selector.Node) {}\n\ntype mockDiscoveries struct {\n\tisSecure bool\n\tnextErr  bool\n\tstopErr  bool\n}\n\nfunc (d *mockDiscoveries) GetService(_ context.Context, _ string) ([]*registry.ServiceInstance, error) {\n\treturn nil, nil\n}\n\nconst errServiceName = \"needErr\"\n\nfunc (d *mockDiscoveries) Watch(ctx context.Context, serviceName string) (registry.Watcher, error) {\n\tif serviceName == errServiceName {\n\t\treturn nil, errors.New(\"mock test service name watch err\")\n\t}\n\treturn &mockWatch{ctx: ctx, isSecure: d.isSecure, nextErr: d.nextErr, stopErr: d.stopErr}, nil\n}\n\ntype mockWatch struct {\n\tctx context.Context\n\n\tisSecure bool\n\tcount    int\n\n\tnextErr bool\n\tstopErr bool\n\n\tlock sync.Mutex\n}\n\nfunc (m *mockWatch) Next() ([]*registry.ServiceInstance, error) {\n\tselect {\n\tcase <-m.ctx.Done():\n\t\treturn nil, m.ctx.Err()\n\tdefault:\n\t}\n\tm.lock.Lock()\n\tdefer m.lock.Unlock()\n\tif m.nextErr {\n\t\treturn nil, errors.New(\"mock test error\")\n\t}\n\tif m.count == 1 {\n\t\treturn nil, errors.New(\"mock test error\")\n\t}\n\tm.count++\n\tinstance := &registry.ServiceInstance{\n\t\tID:        \"1\",\n\t\tName:      \"kratos\",\n\t\tVersion:   \"v1\",\n\t\tMetadata:  map[string]string{},\n\t\tEndpoints: []string{fmt.Sprintf(\"http://127.0.0.1:9001?isSecure=%s\", strconv.FormatBool(m.isSecure))},\n\t}\n\tif m.count > 3 {\n\t\ttime.Sleep(time.Millisecond * 500)\n\t}\n\treturn []*registry.ServiceInstance{instance}, nil\n}\n\nfunc (m *mockWatch) Stop() error {\n\tm.lock.Lock()\n\tdefer m.lock.Unlock()\n\tif m.stopErr {\n\t\treturn errors.New(\"mock test error\")\n\t}\n\t// 标记 next 需要报错\n\tm.nextErr = true\n\treturn nil\n}\n\nfunc TestResolver(t *testing.T) {\n\tta, err := parseTarget(\"discovery://helloworld\", true)\n\tif err != nil {\n\t\tt.Errorf(\"parse err %v\", err)\n\t\treturn\n\t}\n\n\tcancelCtx, cancel := context.WithCancel(context.Background())\n\tdefer cancel()\n\n\t// 异步 无需报错\n\tr, err := newResolver(cancelCtx, &mockDiscoveries{true, false, false}, ta, &mockRebalancer{}, false, false, 25)\n\tif err != nil {\n\t\tt.Errorf(\"expect %v, got %v\", nil, err)\n\t}\n\tif r != nil {\n\t\t_ = r.Close()\n\t}\n\n\t// 同步 一切正常运行\n\tr, err = newResolver(cancelCtx, &mockDiscoveries{false, false, false}, ta, &mockRebalancer{}, true, true, 25)\n\tif err != nil {\n\t\tt.Errorf(\"expect %v, got %v\", nil, err)\n\t}\n\tif r != nil {\n\t\t_ = r.Close()\n\t}\n\n\t// 同步 但是 next 出错 以及 stop 出错\n\tr, err = newResolver(cancelCtx, &mockDiscoveries{false, true, true}, ta, &mockRebalancer{}, true, true, 25)\n\tif err == nil {\n\t\tt.Errorf(\"expect err, got nil\")\n\t}\n\tif r != nil {\n\t\t_ = r.Close()\n\t}\n\n\t// 同步 service name watch 失败\n\tr, err = newResolver(cancelCtx, &mockDiscoveries{false, true, true}, &Target{\n\t\tScheme:   \"discovery\",\n\t\tEndpoint: errServiceName,\n\t}, &mockRebalancer{}, true, true, 25)\n\tif err == nil {\n\t\tt.Errorf(\"expect err, got nil\")\n\t}\n\tif r != nil {\n\t\t_ = r.Close()\n\t}\n\n\tcancel()\n\n\t// 此处应该打印出来 context.Canceled\n\tr, err = newResolver(cancelCtx, &mockDiscoveries{false, false, false}, ta, &mockRebalancer{}, false, false, 25)\n\tif err != nil {\n\t\tt.Errorf(\"expect %v, got %v\", nil, err)\n\t}\n\tif r != nil {\n\t\t_ = r.Close()\n\t}\n\n\t// 同步 但是服务取消，此时需要报错\n\tr, err = newResolver(cancelCtx, &mockDiscoveries{false, false, true}, ta, &mockRebalancer{}, true, true, 25)\n\tif err == nil {\n\t\tt.Errorf(\"expect ctx cancel err, got nil\")\n\t}\n\tif r != nil {\n\t\t_ = r.Close()\n\t}\n\n\ttime.Sleep(100 * time.Millisecond)\n}\n"
  },
  {
    "path": "transport/http/router.go",
    "content": "package http\n\nimport (\n\t\"net/http\"\n\t\"path\"\n)\n\n// WalkRouteFunc is the type of the function called for each route visited by Walk.\ntype WalkRouteFunc func(RouteInfo) error\n\n// RouteInfo is an HTTP route info.\ntype RouteInfo struct {\n\tPath   string\n\tMethod string\n}\n\n// HandlerFunc defines a function to serve HTTP requests.\ntype HandlerFunc func(Context) error\n\n// Router is an HTTP router.\ntype Router struct {\n\tprefix  string\n\tsrv     *Server\n\tfilters []FilterFunc\n}\n\nfunc newRouter(prefix string, srv *Server, filters ...FilterFunc) *Router {\n\tr := &Router{\n\t\tprefix:  prefix,\n\t\tsrv:     srv,\n\t\tfilters: filters,\n\t}\n\treturn r\n}\n\n// Group returns a new router group.\nfunc (r *Router) Group(prefix string, filters ...FilterFunc) *Router {\n\tvar newFilters []FilterFunc\n\tnewFilters = append(newFilters, r.filters...)\n\tnewFilters = append(newFilters, filters...)\n\treturn newRouter(path.Join(r.prefix, prefix), r.srv, newFilters...)\n}\n\n// Handle registers a new route with a matcher for the URL path and method.\nfunc (r *Router) Handle(method, relativePath string, h HandlerFunc, filters ...FilterFunc) {\n\tnext := http.Handler(http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) {\n\t\tctx := &wrapper{router: r}\n\t\tctx.Reset(res, req)\n\t\tif err := h(ctx); err != nil {\n\t\t\tr.srv.ene(res, req, err)\n\t\t}\n\t}))\n\tnext = FilterChain(filters...)(next)\n\tnext = FilterChain(r.filters...)(next)\n\tr.srv.router.Handle(path.Join(r.prefix, relativePath), next).Methods(method)\n}\n\n// GET registers a new GET route for a path with matching handler in the router.\nfunc (r *Router) GET(path string, h HandlerFunc, m ...FilterFunc) {\n\tr.Handle(http.MethodGet, path, h, m...)\n}\n\n// HEAD registers a new HEAD route for a path with matching handler in the router.\nfunc (r *Router) HEAD(path string, h HandlerFunc, m ...FilterFunc) {\n\tr.Handle(http.MethodHead, path, h, m...)\n}\n\n// POST registers a new POST route for a path with matching handler in the router.\nfunc (r *Router) POST(path string, h HandlerFunc, m ...FilterFunc) {\n\tr.Handle(http.MethodPost, path, h, m...)\n}\n\n// PUT registers a new PUT route for a path with matching handler in the router.\nfunc (r *Router) PUT(path string, h HandlerFunc, m ...FilterFunc) {\n\tr.Handle(http.MethodPut, path, h, m...)\n}\n\n// PATCH registers a new PATCH route for a path with matching handler in the router.\nfunc (r *Router) PATCH(path string, h HandlerFunc, m ...FilterFunc) {\n\tr.Handle(http.MethodPatch, path, h, m...)\n}\n\n// DELETE registers a new DELETE route for a path with matching handler in the router.\nfunc (r *Router) DELETE(path string, h HandlerFunc, m ...FilterFunc) {\n\tr.Handle(http.MethodDelete, path, h, m...)\n}\n\n// CONNECT registers a new CONNECT route for a path with matching handler in the router.\nfunc (r *Router) CONNECT(path string, h HandlerFunc, m ...FilterFunc) {\n\tr.Handle(http.MethodConnect, path, h, m...)\n}\n\n// OPTIONS registers a new OPTIONS route for a path with matching handler in the router.\nfunc (r *Router) OPTIONS(path string, h HandlerFunc, m ...FilterFunc) {\n\tr.Handle(http.MethodOptions, path, h, m...)\n}\n\n// TRACE registers a new TRACE route for a path with matching handler in the router.\nfunc (r *Router) TRACE(path string, h HandlerFunc, m ...FilterFunc) {\n\tr.Handle(http.MethodTrace, path, h, m...)\n}\n"
  },
  {
    "path": "transport/http/router_test.go",
    "content": "package http\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"log\"\n\t\"net/http\"\n\t\"reflect\"\n\t\"runtime\"\n\t\"strings\"\n\t\"sync\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/go-kratos/kratos/v2/internal/host\"\n)\n\nconst appJSONStr = \"application/json\"\n\ntype User struct {\n\tName string `json:\"name\"`\n}\n\nfunc corsFilter(next http.Handler) http.Handler {\n\treturn http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\tif r.Method == http.MethodOptions {\n\t\t\tlog.Println(\"cors:\", r.Method, r.RequestURI)\n\t\t\tw.Header().Set(\"Access-Control-Allow-Methods\", r.Method)\n\t\t\treturn\n\t\t}\n\t\tnext.ServeHTTP(w, r)\n\t})\n}\n\nfunc authFilter(next http.Handler) http.Handler {\n\treturn http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\t// Do stuff here\n\t\tlog.Println(\"auth:\", r.Method, r.RequestURI)\n\t\t// Call the next handler, which can be another middleware in the chain, or the final handler.\n\t\tnext.ServeHTTP(w, r)\n\t})\n}\n\nfunc loggingFilter(next http.Handler) http.Handler {\n\treturn http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\t// Do stuff here\n\t\tlog.Println(\"logging:\", r.Method, r.RequestURI)\n\t\t// Call the next handler, which can be another middleware in the chain, or the final handler.\n\t\tnext.ServeHTTP(w, r)\n\t})\n}\n\nfunc TestRoute(t *testing.T) {\n\tctx := context.Background()\n\tsrv := NewServer(\n\t\tFilter(corsFilter, loggingFilter),\n\t)\n\troute := srv.Route(\"/v1\")\n\troute.GET(\"/users/{name}\", func(ctx Context) error {\n\t\tu := new(User)\n\t\tu.Name = ctx.Vars().Get(\"name\")\n\t\treturn ctx.Result(200, u)\n\t}, authFilter)\n\troute.POST(\"/users\", func(ctx Context) error {\n\t\tu := new(User)\n\t\tif err := ctx.Bind(u); err != nil {\n\t\t\treturn err\n\t\t}\n\t\treturn ctx.Result(201, u)\n\t})\n\troute.PUT(\"/users\", func(ctx Context) error {\n\t\tu := new(User)\n\t\tif err := ctx.Bind(u); err != nil {\n\t\t\treturn err\n\t\t}\n\t\th := ctx.Middleware(func(context.Context, any) (any, error) {\n\t\t\treturn u, nil\n\t\t})\n\t\treturn ctx.Returns(h(ctx, u))\n\t})\n\n\tif e, err := srv.Endpoint(); err != nil || e == nil {\n\t\tt.Fatal(e, err)\n\t}\n\tgo func() {\n\t\tif err := srv.Start(ctx); err != nil {\n\t\t\tpanic(err)\n\t\t}\n\t}()\n\ttime.Sleep(time.Second)\n\ttestRoute(t, srv)\n\t_ = srv.Stop(ctx)\n}\n\nfunc testRoute(t *testing.T, srv *Server) {\n\tport, ok := host.Port(srv.lis)\n\tif !ok {\n\t\tt.Fatalf(\"extract port error: %v\", srv.lis)\n\t}\n\tbase := fmt.Sprintf(\"http://127.0.0.1:%d/v1\", port)\n\t// GET\n\tresp, err := http.Get(base + \"/users/foo\")\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tdefer resp.Body.Close()\n\tif resp.StatusCode != 200 {\n\t\tt.Fatalf(\"code: %d\", resp.StatusCode)\n\t}\n\tif v := resp.Header.Get(\"Content-Type\"); v != appJSONStr {\n\t\tt.Fatalf(\"contentType: %s\", v)\n\t}\n\tu := new(User)\n\tif err = json.NewDecoder(resp.Body).Decode(u); err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif u.Name != \"foo\" {\n\t\tt.Fatalf(\"got %s want foo\", u.Name)\n\t}\n\t// POST\n\tresp, err = http.Post(base+\"/users\", appJSONStr, strings.NewReader(`{\"name\":\"bar\"}`))\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tdefer resp.Body.Close()\n\tif resp.StatusCode != 201 {\n\t\tt.Fatalf(\"code: %d\", resp.StatusCode)\n\t}\n\tif v := resp.Header.Get(\"Content-Type\"); v != appJSONStr {\n\t\tt.Fatalf(\"contentType: %s\", v)\n\t}\n\tu = new(User)\n\tif err = json.NewDecoder(resp.Body).Decode(u); err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif u.Name != \"bar\" {\n\t\tt.Fatalf(\"got %s want bar\", u.Name)\n\t}\n\t// PUT\n\treq, _ := http.NewRequest(http.MethodPut, base+\"/users\", strings.NewReader(`{\"name\":\"bar\"}`))\n\treq.Header.Set(\"Content-Type\", appJSONStr)\n\tresp, err = http.DefaultClient.Do(req)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tdefer resp.Body.Close()\n\tif resp.StatusCode != 200 {\n\t\tt.Fatalf(\"code: %d\", resp.StatusCode)\n\t}\n\tif v := resp.Header.Get(\"Content-Type\"); v != appJSONStr {\n\t\tt.Fatalf(\"contentType: %s\", v)\n\t}\n\tu = new(User)\n\tif err = json.NewDecoder(resp.Body).Decode(u); err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif u.Name != \"bar\" {\n\t\tt.Fatalf(\"got %s want bar\", u.Name)\n\t}\n\t// OPTIONS\n\treq, _ = http.NewRequest(http.MethodOptions, base+\"/users\", nil)\n\tresp, err = http.DefaultClient.Do(req)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tdefer resp.Body.Close()\n\tif resp.StatusCode != 200 {\n\t\tt.Fatalf(\"code: %d\", resp.StatusCode)\n\t}\n\tif resp.Header.Get(\"Access-Control-Allow-Methods\") != http.MethodOptions {\n\t\tt.Fatal(\"cors failed\")\n\t}\n}\n\nfunc TestRouter_Group(t *testing.T) {\n\tr := &Router{}\n\trr := r.Group(\"a\", func(http.Handler) http.Handler { return nil })\n\tif !reflect.DeepEqual(\"a\", rr.prefix) {\n\t\tt.Errorf(\"expected %q, got %q\", \"a\", rr.prefix)\n\t}\n}\n\nfunc TestHandle(_ *testing.T) {\n\tr := newRouter(\"/\", NewServer())\n\th := func(Context) error {\n\t\treturn nil\n\t}\n\tr.GET(\"/get\", h)\n\tr.HEAD(\"/head\", h)\n\tr.PATCH(\"/patch\", h)\n\tr.DELETE(\"/delete\", h)\n\tr.CONNECT(\"/connect\", h)\n\tr.OPTIONS(\"/options\", h)\n\tr.TRACE(\"/trace\", h)\n}\n\nfunc TestRouter_ContextDataRace(t *testing.T) {\n\truntime.GOMAXPROCS(runtime.NumCPU())\n\n\tctx := context.Background()\n\tsrvPort := 38888\n\tsrvAddr := fmt.Sprintf(\":%d\", srvPort)\n\tsrv := NewServer(Timeout(time.Millisecond*50), Address(srvAddr))\n\n\trouter := srv.Route(\"/\")\n\trouter.GET(\"/ping\", func(ctx Context) error {\n\t\treq, _ := http.NewRequestWithContext(ctx, http.MethodGet, \"http://www.baidu.com\", nil)\n\t\tresp, err := http.DefaultClient.Do(req)\n\t\tif err != nil {\n\t\t\treturn ctx.String(200, err.Error())\n\t\t}\n\t\t_ = resp.Body.Close()\n\t\treturn ctx.String(200, \"pong\")\n\t})\n\n\t// start server\n\tgo func() {\n\t\tif err := srv.Start(ctx); err != nil {\n\t\t\tif errors.Is(err, http.ErrServerClosed) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tpanic(err)\n\t\t}\n\t}()\n\n\ttime.Sleep(time.Second)\n\n\t// start client\n\tworkers := 10\n\twg := sync.WaitGroup{}\n\twg.Add(workers)\n\tfor i := 0; i < workers; i++ {\n\t\tgo func() {\n\t\t\tdefer wg.Done()\n\t\t\tfor j := 0; j < 50; j++ {\n\t\t\t\treq, _ := http.NewRequestWithContext(ctx, http.MethodGet, fmt.Sprintf(\"http://127.0.0.1:%d/ping\", srvPort), nil)\n\t\t\t\tres, err := http.DefaultClient.Do(req)\n\t\t\t\tif err != nil {\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t\t_ = res.Body.Close()\n\t\t\t}\n\t\t}()\n\t}\n\twg.Wait()\n\t_ = srv.Stop(ctx)\n\tt.Log(\"test end\")\n}\n"
  },
  {
    "path": "transport/http/server.go",
    "content": "package http\n\nimport (\n\t\"context\"\n\t\"crypto/tls\"\n\t\"errors\"\n\t\"net\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"time\"\n\n\t\"github.com/gorilla/mux\"\n\n\t\"github.com/go-kratos/kratos/v2/internal/endpoint\"\n\t\"github.com/go-kratos/kratos/v2/internal/host\"\n\t\"github.com/go-kratos/kratos/v2/internal/matcher\"\n\t\"github.com/go-kratos/kratos/v2/log\"\n\t\"github.com/go-kratos/kratos/v2/middleware\"\n\t\"github.com/go-kratos/kratos/v2/transport\"\n)\n\nvar (\n\t_ transport.Server     = (*Server)(nil)\n\t_ transport.Endpointer = (*Server)(nil)\n\t_ http.Handler         = (*Server)(nil)\n)\n\n// ServerOption is an HTTP server option.\ntype ServerOption func(*Server)\n\n// Network with server network.\nfunc Network(network string) ServerOption {\n\treturn func(s *Server) {\n\t\ts.network = network\n\t}\n}\n\n// Address with server address.\nfunc Address(addr string) ServerOption {\n\treturn func(s *Server) {\n\t\ts.address = addr\n\t}\n}\n\n// Endpoint with server address.\nfunc Endpoint(endpoint *url.URL) ServerOption {\n\treturn func(s *Server) {\n\t\ts.endpoint = endpoint\n\t}\n}\n\n// Timeout with server timeout.\nfunc Timeout(timeout time.Duration) ServerOption {\n\treturn func(s *Server) {\n\t\ts.timeout = timeout\n\t}\n}\n\n// Logger with server logger.\n// Deprecated: use global logger instead.\nfunc Logger(log.Logger) ServerOption {\n\treturn func(*Server) {}\n}\n\n// Middleware with service middleware option.\nfunc Middleware(m ...middleware.Middleware) ServerOption {\n\treturn func(o *Server) {\n\t\to.middleware.Use(m...)\n\t}\n}\n\n// Filter with HTTP middleware option.\nfunc Filter(filters ...FilterFunc) ServerOption {\n\treturn func(o *Server) {\n\t\to.filters = filters\n\t}\n}\n\n// RequestVarsDecoder with request decoder.\nfunc RequestVarsDecoder(dec DecodeRequestFunc) ServerOption {\n\treturn func(o *Server) {\n\t\to.decVars = dec\n\t}\n}\n\n// RequestQueryDecoder with request decoder.\nfunc RequestQueryDecoder(dec DecodeRequestFunc) ServerOption {\n\treturn func(o *Server) {\n\t\to.decQuery = dec\n\t}\n}\n\n// RequestDecoder with request decoder.\nfunc RequestDecoder(dec DecodeRequestFunc) ServerOption {\n\treturn func(o *Server) {\n\t\to.decBody = dec\n\t}\n}\n\n// ResponseEncoder with response encoder.\nfunc ResponseEncoder(en EncodeResponseFunc) ServerOption {\n\treturn func(o *Server) {\n\t\to.enc = en\n\t}\n}\n\n// ErrorEncoder with error encoder.\nfunc ErrorEncoder(en EncodeErrorFunc) ServerOption {\n\treturn func(o *Server) {\n\t\to.ene = en\n\t}\n}\n\n// TLSConfig with TLS config.\nfunc TLSConfig(c *tls.Config) ServerOption {\n\treturn func(o *Server) {\n\t\to.tlsConf = c\n\t}\n}\n\n// StrictSlash is with mux's StrictSlash\n// If true, when the path pattern is \"/path/\", accessing \"/path\" will\n// redirect to the former and vice versa.\nfunc StrictSlash(strictSlash bool) ServerOption {\n\treturn func(o *Server) {\n\t\to.strictSlash = strictSlash\n\t}\n}\n\n// Listener with server lis\nfunc Listener(lis net.Listener) ServerOption {\n\treturn func(s *Server) {\n\t\ts.lis = lis\n\t}\n}\n\n// PathPrefix with mux's PathPrefix, router will be replaced by a subrouter that start with prefix.\nfunc PathPrefix(prefix string) ServerOption {\n\treturn func(s *Server) {\n\t\ts.router = s.router.PathPrefix(prefix).Subrouter()\n\t}\n}\n\nfunc NotFoundHandler(handler http.Handler) ServerOption {\n\treturn func(s *Server) {\n\t\ts.router.NotFoundHandler = handler\n\t}\n}\n\nfunc MethodNotAllowedHandler(handler http.Handler) ServerOption {\n\treturn func(s *Server) {\n\t\ts.router.MethodNotAllowedHandler = handler\n\t}\n}\n\n// Server is an HTTP server wrapper.\ntype Server struct {\n\t*http.Server\n\tlis         net.Listener\n\ttlsConf     *tls.Config\n\tendpoint    *url.URL\n\terr         error\n\tnetwork     string\n\taddress     string\n\ttimeout     time.Duration\n\tfilters     []FilterFunc\n\tmiddleware  matcher.Matcher\n\tdecVars     DecodeRequestFunc\n\tdecQuery    DecodeRequestFunc\n\tdecBody     DecodeRequestFunc\n\tenc         EncodeResponseFunc\n\tene         EncodeErrorFunc\n\tstrictSlash bool\n\trouter      *mux.Router\n}\n\n// NewServer creates an HTTP server by options.\nfunc NewServer(opts ...ServerOption) *Server {\n\tsrv := &Server{\n\t\tnetwork:     \"tcp\",\n\t\taddress:     \":0\",\n\t\ttimeout:     1 * time.Second,\n\t\tmiddleware:  matcher.New(),\n\t\tdecVars:     DefaultRequestVars,\n\t\tdecQuery:    DefaultRequestQuery,\n\t\tdecBody:     DefaultRequestDecoder,\n\t\tenc:         DefaultResponseEncoder,\n\t\tene:         DefaultErrorEncoder,\n\t\tstrictSlash: true,\n\t\trouter:      mux.NewRouter(),\n\t}\n\tsrv.router.NotFoundHandler = http.DefaultServeMux\n\tsrv.router.MethodNotAllowedHandler = http.DefaultServeMux\n\tfor _, o := range opts {\n\t\to(srv)\n\t}\n\tsrv.router.StrictSlash(srv.strictSlash)\n\tsrv.router.Use(srv.filter())\n\tsrv.Server = &http.Server{\n\t\tHandler:   FilterChain(srv.filters...)(srv.router),\n\t\tTLSConfig: srv.tlsConf,\n\t}\n\treturn srv\n}\n\n// Use uses a service middleware with selector.\n// selector:\n//   - '/*'\n//   - '/helloworld.v1.Greeter/*'\n//   - '/helloworld.v1.Greeter/SayHello'\nfunc (s *Server) Use(selector string, m ...middleware.Middleware) {\n\ts.middleware.Add(selector, m...)\n}\n\n// WalkRoute walks the router and all its sub-routers, calling walkFn for each route in the tree.\nfunc (s *Server) WalkRoute(fn WalkRouteFunc) error {\n\treturn s.router.Walk(func(route *mux.Route, _ *mux.Router, _ []*mux.Route) error {\n\t\tmethods, err := route.GetMethods()\n\t\tif err != nil {\n\t\t\treturn nil // ignore no methods\n\t\t}\n\t\tpath, err := route.GetPathTemplate()\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tfor _, method := range methods {\n\t\t\tif err := fn(RouteInfo{Method: method, Path: path}); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t\treturn nil\n\t})\n}\n\n// WalkHandle walks the router and all its sub-routers, calling walkFn for each route in the tree.\nfunc (s *Server) WalkHandle(handle func(method, path string, handler http.HandlerFunc)) error {\n\treturn s.WalkRoute(func(r RouteInfo) error {\n\t\thandle(r.Method, r.Path, s.ServeHTTP)\n\t\treturn nil\n\t})\n}\n\n// Route registers an HTTP router.\nfunc (s *Server) Route(prefix string, filters ...FilterFunc) *Router {\n\treturn newRouter(prefix, s, filters...)\n}\n\n// Handle registers a new route with a matcher for the URL path.\nfunc (s *Server) Handle(path string, h http.Handler) {\n\ts.router.Handle(path, h)\n}\n\n// HandlePrefix registers a new route with a matcher for the URL path prefix.\nfunc (s *Server) HandlePrefix(prefix string, h http.Handler) {\n\ts.router.PathPrefix(prefix).Handler(h)\n}\n\n// HandleFunc registers a new route with a matcher for the URL path.\nfunc (s *Server) HandleFunc(path string, h http.HandlerFunc) {\n\ts.router.HandleFunc(path, h)\n}\n\n// HandleHeader registers a new route with a matcher for the header.\nfunc (s *Server) HandleHeader(key, val string, h http.HandlerFunc) {\n\ts.router.Headers(key, val).Handler(h)\n}\n\n// ServeHTTP should write reply headers and data to the ResponseWriter and then return.\nfunc (s *Server) ServeHTTP(res http.ResponseWriter, req *http.Request) {\n\ts.Handler.ServeHTTP(res, req)\n}\n\nfunc (s *Server) filter() mux.MiddlewareFunc {\n\treturn func(next http.Handler) http.Handler {\n\t\treturn http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {\n\t\t\tvar (\n\t\t\t\tctx    context.Context\n\t\t\t\tcancel context.CancelFunc\n\t\t\t)\n\t\t\tif s.timeout > 0 {\n\t\t\t\tctx, cancel = context.WithTimeout(req.Context(), s.timeout)\n\t\t\t} else {\n\t\t\t\tctx, cancel = context.WithCancel(req.Context())\n\t\t\t}\n\t\t\tdefer cancel()\n\n\t\t\tpathTemplate := req.URL.Path\n\t\t\tif route := mux.CurrentRoute(req); route != nil {\n\t\t\t\t// /path/123 -> /path/{id}\n\t\t\t\tpathTemplate, _ = route.GetPathTemplate()\n\t\t\t}\n\n\t\t\ttr := &Transport{\n\t\t\t\toperation:    pathTemplate,\n\t\t\t\tpathTemplate: pathTemplate,\n\t\t\t\treqHeader:    headerCarrier(req.Header),\n\t\t\t\treplyHeader:  headerCarrier(w.Header()),\n\t\t\t\trequest:      req,\n\t\t\t\tresponse:     w,\n\t\t\t}\n\t\t\tif s.endpoint != nil {\n\t\t\t\ttr.endpoint = s.endpoint.String()\n\t\t\t}\n\t\t\ttr.request = req.WithContext(transport.NewServerContext(ctx, tr))\n\t\t\tnext.ServeHTTP(w, tr.request)\n\t\t})\n\t}\n}\n\n// Endpoint return a real address to registry endpoint.\n// examples:\n//\n//\thttps://127.0.0.1:8000\n//\tLegacy: http://127.0.0.1:8000?isSecure=false\nfunc (s *Server) Endpoint() (*url.URL, error) {\n\tif err := s.listenAndEndpoint(); err != nil {\n\t\treturn nil, err\n\t}\n\treturn s.endpoint, nil\n}\n\n// Start start the HTTP server.\nfunc (s *Server) Start(ctx context.Context) error {\n\tif err := s.listenAndEndpoint(); err != nil {\n\t\treturn err\n\t}\n\ts.BaseContext = func(net.Listener) context.Context {\n\t\treturn ctx\n\t}\n\tlog.Infof(\"[HTTP] server listening on: %s\", s.lis.Addr().String())\n\tvar err error\n\tif s.tlsConf != nil {\n\t\terr = s.ServeTLS(s.lis, \"\", \"\")\n\t} else {\n\t\terr = s.Serve(s.lis)\n\t}\n\tif !errors.Is(err, http.ErrServerClosed) {\n\t\treturn err\n\t}\n\treturn nil\n}\n\n// Stop stop the HTTP server.\nfunc (s *Server) Stop(ctx context.Context) error {\n\tlog.Info(\"[HTTP] server stopping\")\n\terr := s.Shutdown(ctx)\n\tif err != nil {\n\t\tif ctx.Err() != nil {\n\t\t\tlog.Warn(\"[HTTP] server couldn't stop gracefully in time, doing force stop\")\n\t\t\terr = s.Close()\n\t\t}\n\t}\n\treturn err\n}\n\nfunc (s *Server) listenAndEndpoint() error {\n\tif s.lis == nil {\n\t\tlis, err := net.Listen(s.network, s.address)\n\t\tif err != nil {\n\t\t\ts.err = err\n\t\t\treturn err\n\t\t}\n\t\ts.lis = lis\n\t}\n\tif s.endpoint == nil {\n\t\taddr, err := host.Extract(s.address, s.lis)\n\t\tif err != nil {\n\t\t\ts.err = err\n\t\t\treturn err\n\t\t}\n\t\ts.endpoint = endpoint.NewEndpoint(endpoint.Scheme(\"http\", s.tlsConf != nil), addr)\n\t}\n\treturn s.err\n}\n"
  },
  {
    "path": "transport/http/server_test.go",
    "content": "package http\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"crypto/tls\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"net\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"reflect\"\n\t\"strings\"\n\t\"sync\"\n\t\"testing\"\n\t\"time\"\n\n\tkratoserrors \"github.com/go-kratos/kratos/v2/errors\"\n\t\"github.com/go-kratos/kratos/v2/internal/host\"\n\t\"github.com/go-kratos/kratos/v2/log\"\n)\n\nvar h = func(w http.ResponseWriter, r *http.Request) {\n\t_ = json.NewEncoder(w).Encode(testData{Path: r.RequestURI})\n}\n\ntype testKey struct{}\n\ntype testData struct {\n\tPath string `json:\"path\"`\n}\n\n// handleFuncWrapper is a wrapper for http.HandlerFunc to implement http.Handler\ntype handleFuncWrapper struct {\n\tfn http.HandlerFunc\n}\n\nfunc (x *handleFuncWrapper) ServeHTTP(writer http.ResponseWriter, request *http.Request) {\n\tx.fn.ServeHTTP(writer, request)\n}\n\nfunc newHandleFuncWrapper(fn http.HandlerFunc) http.Handler {\n\treturn &handleFuncWrapper{fn: fn}\n}\n\nfunc TestServeHTTP(t *testing.T) {\n\tln, err := net.Listen(\"tcp\", \":0\")\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tmux := NewServer(Listener(ln))\n\tmux.HandleFunc(\"/index\", h)\n\tmux.Route(\"/errors\").GET(\"/cause\", func(Context) error {\n\t\treturn kratoserrors.BadRequest(\"xxx\", \"zzz\").\n\t\t\tWithMetadata(map[string]string{\"foo\": \"bar\"}).\n\t\t\tWithCause(errors.New(\"error cause\"))\n\t})\n\tif err = mux.WalkRoute(func(r RouteInfo) error {\n\t\tt.Logf(\"WalkRoute: %+v\", r)\n\t\treturn nil\n\t}); err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif e, err := mux.Endpoint(); err != nil || e == nil || strings.HasSuffix(e.Host, \":0\") {\n\t\tt.Fatal(e, err)\n\t}\n\tsrv := http.Server{Handler: mux}\n\tgo func() {\n\t\tif err := srv.Serve(ln); err != nil {\n\t\t\tif kratoserrors.Is(err, http.ErrServerClosed) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tpanic(err)\n\t\t}\n\t}()\n\ttime.Sleep(time.Second)\n\tif err := srv.Shutdown(context.Background()); err != nil {\n\t\tt.Log(err)\n\t}\n}\n\nfunc TestServer(t *testing.T) {\n\tctx := context.Background()\n\tsrv := NewServer()\n\tsrv.Handle(\"/index\", newHandleFuncWrapper(h))\n\tsrv.HandleFunc(\"/index/{id:[0-9]+}\", h)\n\tsrv.HandlePrefix(\"/test/prefix\", newHandleFuncWrapper(h))\n\tsrv.HandleHeader(\"content-type\", \"application/grpc-web+json\", func(w http.ResponseWriter, r *http.Request) {\n\t\t_ = json.NewEncoder(w).Encode(testData{Path: r.RequestURI})\n\t})\n\tsrv.Route(\"/errors\").GET(\"/cause\", func(Context) error {\n\t\treturn kratoserrors.BadRequest(\"xxx\", \"zzz\").\n\t\t\tWithMetadata(map[string]string{\"foo\": \"bar\"}).\n\t\t\tWithCause(errors.New(\"error cause\"))\n\t})\n\n\tif e, err := srv.Endpoint(); err != nil || e == nil || strings.HasSuffix(e.Host, \":0\") {\n\t\tt.Fatal(e, err)\n\t}\n\n\tgo func() {\n\t\tif err := srv.Start(ctx); err != nil {\n\t\t\tpanic(err)\n\t\t}\n\t}()\n\ttime.Sleep(time.Second)\n\ttestHeader(t, srv)\n\ttestClient(t, srv)\n\ttestAccept(t, srv)\n\ttime.Sleep(time.Second)\n\tif srv.Stop(ctx) != nil {\n\t\tt.Errorf(\"expected nil got %v\", srv.Stop(ctx))\n\t}\n}\n\nfunc testAccept(t *testing.T, srv *Server) {\n\ttests := []struct {\n\t\tmethod      string\n\t\tpath        string\n\t\tcontentType string\n\t}{\n\t\t{http.MethodGet, \"/errors/cause\", \"application/json\"},\n\t\t{http.MethodGet, \"/errors/cause\", \"application/proto\"},\n\t}\n\te, err := srv.Endpoint()\n\tif err != nil {\n\t\tt.Errorf(\"expected nil got %v\", err)\n\t}\n\tclient, err := NewClient(context.Background(), WithEndpoint(e.Host))\n\tif err != nil {\n\t\tt.Errorf(\"expected nil got %v\", err)\n\t}\n\tfor _, test := range tests {\n\t\treq, err := http.NewRequest(test.method, e.String()+test.path, nil)\n\t\tif err != nil {\n\t\t\tt.Errorf(\"expected nil got %v\", err)\n\t\t}\n\t\treq.Header.Set(\"Content-Type\", test.contentType)\n\t\tresp, err := client.Do(req)\n\t\tif kratoserrors.Code(err) != 400 {\n\t\t\tt.Errorf(\"expected 400 got %v\", err)\n\t\t}\n\t\tif err == nil {\n\t\t\tresp.Body.Close()\n\t\t}\n\t}\n}\n\nfunc testHeader(t *testing.T, srv *Server) {\n\te, err := srv.Endpoint()\n\tif err != nil {\n\t\tt.Errorf(\"expected nil got %v\", err)\n\t}\n\tclient, err := NewClient(context.Background(), WithEndpoint(e.Host))\n\tif err != nil {\n\t\tt.Errorf(\"expected nil got %v\", err)\n\t}\n\treq, err := http.NewRequest(http.MethodGet, e.String()+\"/index\", nil)\n\tif err != nil {\n\t\tt.Errorf(\"expected nil got %v\", err)\n\t}\n\treq.Header.Set(\"content-type\", \"application/grpc-web+json\")\n\tresp, err := client.Do(req)\n\tif err != nil {\n\t\tt.Errorf(\"expected nil got %v\", err)\n\t}\n\tresp.Body.Close()\n}\n\nfunc testClient(t *testing.T, srv *Server) {\n\ttests := []struct {\n\t\tmethod string\n\t\tpath   string\n\t\tcode   int\n\t}{\n\t\t{http.MethodGet, \"/index\", http.StatusOK},\n\t\t{http.MethodPut, \"/index\", http.StatusOK},\n\t\t{http.MethodPost, \"/index\", http.StatusOK},\n\t\t{http.MethodPatch, \"/index\", http.StatusOK},\n\t\t{http.MethodDelete, \"/index\", http.StatusOK},\n\n\t\t{http.MethodGet, \"/index/1\", http.StatusOK},\n\t\t{http.MethodPut, \"/index/1\", http.StatusOK},\n\t\t{http.MethodPost, \"/index/1\", http.StatusOK},\n\t\t{http.MethodPatch, \"/index/1\", http.StatusOK},\n\t\t{http.MethodDelete, \"/index/1\", http.StatusOK},\n\n\t\t{http.MethodGet, \"/index/notfound\", http.StatusNotFound},\n\t\t{http.MethodGet, \"/errors/cause\", http.StatusBadRequest},\n\t\t{http.MethodGet, \"/test/prefix/123111\", http.StatusOK},\n\t}\n\te, err := srv.Endpoint()\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tclient, err := NewClient(context.Background(), WithEndpoint(e.Host))\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tdefer client.Close()\n\tfor _, test := range tests {\n\t\tvar res testData\n\t\treqURL := e.String() + test.path\n\t\treq, err := http.NewRequest(test.method, reqURL, nil)\n\t\tif err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\t\tresp, err := client.Do(req)\n\t\tif kratoserrors.Code(err) != test.code {\n\t\t\tt.Fatalf(\"want %v, but got %v\", test, err)\n\t\t}\n\t\tif err != nil {\n\t\t\tcontinue\n\t\t}\n\t\tif resp.StatusCode != 200 {\n\t\t\t_ = resp.Body.Close()\n\t\t\tt.Fatalf(\"http status got %d\", resp.StatusCode)\n\t\t}\n\t\tcontent, err := io.ReadAll(resp.Body)\n\t\t_ = resp.Body.Close()\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"read resp error %v\", err)\n\t\t}\n\t\terr = json.Unmarshal(content, &res)\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"unmarshal resp error %v\", err)\n\t\t}\n\t\tif res.Path != test.path {\n\t\t\tt.Errorf(\"expected %s got %s\", test.path, res.Path)\n\t\t}\n\t}\n\tfor _, test := range tests {\n\t\tvar res testData\n\t\terr := client.Invoke(context.Background(), test.method, test.path, nil, &res)\n\t\tif kratoserrors.Code(err) != test.code {\n\t\t\tt.Fatalf(\"want %v, but got %v\", test, err)\n\t\t}\n\t\tif err != nil {\n\t\t\tcontinue\n\t\t}\n\t\tif res.Path != test.path {\n\t\t\tt.Errorf(\"expected %s got %s\", test.path, res.Path)\n\t\t}\n\t}\n}\n\nfunc BenchmarkServer(b *testing.B) {\n\tfn := func(w http.ResponseWriter, r *http.Request) {\n\t\tdata := &testData{Path: r.RequestURI}\n\t\t_ = json.NewEncoder(w).Encode(data)\n\t\tif r.Context().Value(testKey{}) != \"test\" {\n\t\t\tw.WriteHeader(500)\n\t\t}\n\t}\n\tctx := context.Background()\n\tctx = context.WithValue(ctx, testKey{}, \"test\")\n\tsrv := NewServer()\n\tsrv.HandleFunc(\"/index\", fn)\n\tgo func() {\n\t\tif err := srv.Start(ctx); err != nil {\n\t\t\tpanic(err)\n\t\t}\n\t}()\n\ttime.Sleep(time.Second)\n\tport, ok := host.Port(srv.lis)\n\tif !ok {\n\t\tb.Errorf(\"expected port got %v\", srv.lis)\n\t}\n\tclient, err := NewClient(context.Background(), WithEndpoint(fmt.Sprintf(\"127.0.0.1:%d\", port)))\n\tif err != nil {\n\t\tb.Errorf(\"expected nil got %v\", err)\n\t}\n\n\tb.ResetTimer()\n\tfor i := 0; i < b.N; i++ {\n\t\tvar res testData\n\t\terr := client.Invoke(context.Background(), http.MethodPost, \"/index\", nil, &res)\n\t\tif err != nil {\n\t\t\tb.Errorf(\"expected nil got %v\", err)\n\t\t}\n\t}\n\t_ = srv.Stop(ctx)\n}\n\nfunc TestNetwork(t *testing.T) {\n\to := &Server{}\n\tv := \"abc\"\n\tNetwork(v)(o)\n\tif !reflect.DeepEqual(v, o.network) {\n\t\tt.Errorf(\"expected %v got %v\", v, o.network)\n\t}\n}\n\nfunc TestAddress(t *testing.T) {\n\to := &Server{}\n\tv := \"abc\"\n\tAddress(v)(o)\n\tif !reflect.DeepEqual(v, o.address) {\n\t\tt.Errorf(\"expected %v got %v\", v, o.address)\n\t}\n}\n\nfunc TestTimeout(t *testing.T) {\n\to := &Server{}\n\tv := time.Duration(123)\n\tTimeout(v)(o)\n\tif !reflect.DeepEqual(v, o.timeout) {\n\t\tt.Errorf(\"expected %v got %v\", v, o.timeout)\n\t}\n}\n\nfunc TestRequestDecoder(t *testing.T) {\n\to := &Server{}\n\tv := func(*http.Request, any) error { return nil }\n\tRequestDecoder(v)(o)\n\tif o.decBody == nil {\n\t\tt.Errorf(\"expected nil got %v\", o.decBody)\n\t}\n}\n\nfunc TestResponseEncoder(t *testing.T) {\n\to := &Server{}\n\tv := func(http.ResponseWriter, *http.Request, any) error { return nil }\n\tResponseEncoder(v)(o)\n\tif o.enc == nil {\n\t\tt.Errorf(\"expected nil got %v\", o.enc)\n\t}\n}\n\nfunc TestErrorEncoder(t *testing.T) {\n\to := &Server{}\n\tv := func(http.ResponseWriter, *http.Request, error) {}\n\tErrorEncoder(v)(o)\n\tif o.ene == nil {\n\t\tt.Errorf(\"expected nil got %v\", o.ene)\n\t}\n}\n\nfunc TestTLSConfig(t *testing.T) {\n\to := &Server{}\n\tv := &tls.Config{}\n\tTLSConfig(v)(o)\n\tif !reflect.DeepEqual(v, o.tlsConf) {\n\t\tt.Errorf(\"expected %v got %v\", v, o.tlsConf)\n\t}\n}\n\nfunc TestStrictSlash(t *testing.T) {\n\to := &Server{}\n\tv := true\n\tStrictSlash(v)(o)\n\tif !reflect.DeepEqual(v, o.strictSlash) {\n\t\tt.Errorf(\"expected %v got %v\", v, o.tlsConf)\n\t}\n}\n\nfunc TestListener(t *testing.T) {\n\tlis, err := net.Listen(\"tcp\", \":0\")\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\ts := &Server{}\n\tListener(lis)(s)\n\tif !reflect.DeepEqual(s.lis, lis) {\n\t\tt.Errorf(\"expected %v got %v\", lis, s.lis)\n\t}\n\tif e, err := s.Endpoint(); err != nil || e == nil {\n\t\tt.Errorf(\"expected not empty\")\n\t}\n}\n\nfunc TestNotFoundHandler(t *testing.T) {\n\tmux := http.NewServeMux()\n\tsrv := NewServer(NotFoundHandler(mux))\n\tif !reflect.DeepEqual(srv.router.NotFoundHandler, mux) {\n\t\tt.Errorf(\"expected %v got %v\", mux, srv.router.NotFoundHandler)\n\t}\n}\n\nfunc TestMethodNotAllowedHandler(t *testing.T) {\n\tmux := http.NewServeMux()\n\tsrv := NewServer(MethodNotAllowedHandler(mux))\n\tif !reflect.DeepEqual(srv.router.MethodNotAllowedHandler, mux) {\n\t\tt.Errorf(\"expected %v got %v\", mux, srv.router.MethodNotAllowedHandler)\n\t}\n}\n\nfunc TestStop(t *testing.T) {\n\ttimeoutCtx, cancel := context.WithTimeout(context.Background(), 10*time.Millisecond)\n\tdefer cancel()\n\n\ttests := []struct {\n\t\tname          string\n\t\tsleep         time.Duration\n\t\tctx           context.Context\n\t\tcancel        context.CancelFunc\n\t\twantForceStop bool\n\t}{\n\t\t{\n\t\t\tname:          \"normal\",\n\t\t\tsleep:         0,\n\t\t\tctx:           context.Background(),\n\t\t\tcancel:        func() {},\n\t\t\twantForceStop: false,\n\t\t},\n\t\t{\n\t\t\tname:          \"timeout\",\n\t\t\tsleep:         2 * time.Second,\n\t\t\tctx:           timeoutCtx,\n\t\t\tcancel:        cancel,\n\t\t\twantForceStop: true,\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\told := log.GetLogger()\n\t\t\tdefer log.SetLogger(old)\n\n\t\t\t// Create a logger to capture logs\n\t\t\tvar logs safeBytesBuffer\n\t\t\tlog.SetLogger(log.NewStdLogger(&logs))\n\n\t\t\ttestServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\t\t\tt := time.NewTimer(tt.sleep)\n\t\t\t\tdefer t.Stop()\n\t\t\t\tselect {\n\t\t\t\tcase <-t.C:\n\t\t\t\tcase <-r.Context().Done():\n\t\t\t\t}\n\t\t\t\tw.WriteHeader(http.StatusOK)\n\t\t\t}))\n\t\t\tdefer testServer.Close()\n\n\t\t\tgo func() {\n\t\t\t\tresp, err := http.Get(testServer.URL)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\t_ = resp.Body.Close()\n\t\t\t}()\n\n\t\t\ttime.Sleep(100 * time.Millisecond)\n\n\t\t\ts := &Server{\n\t\t\t\tServer: testServer.Config,\n\t\t\t}\n\n\t\t\ttt.cancel()\n\t\t\terr := s.Stop(tt.ctx)\n\t\t\tif err != nil {\n\t\t\t\tt.Errorf(\"Expected no error, got %v\", err)\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\t// Check if the stop was forced or graceful\n\t\t\tif tt.wantForceStop {\n\t\t\t\tif !strings.Contains(logs.String(), \"force stop\") {\n\t\t\t\t\tt.Errorf(\"Expected force stop\\n%s\", logs.String())\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tif strings.Contains(logs.String(), \"force stop\") {\n\t\t\t\t\tt.Errorf(\"Expected graceful stop\\n%s\", logs.String())\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t}\n}\n\ntype safeBytesBuffer struct {\n\tmu  sync.Mutex\n\tbuf bytes.Buffer\n}\n\nfunc (b *safeBytesBuffer) Write(p []byte) (n int, err error) {\n\tb.mu.Lock()\n\tdefer b.mu.Unlock()\n\treturn b.buf.Write(p)\n}\n\nfunc (b *safeBytesBuffer) String() string {\n\tb.mu.Lock()\n\tdefer b.mu.Unlock()\n\treturn b.buf.String()\n}\n"
  },
  {
    "path": "transport/http/status/status.go",
    "content": "package status\n\nimport (\n\t\"net/http\"\n\n\t\"google.golang.org/grpc/codes\"\n)\n\nconst (\n\t// ClientClosed is non-standard http status code,\n\t// which defined by nginx.\n\t// https://httpstatus.in/499/\n\tClientClosed = 499\n)\n\n// Converter is a status converter.\ntype Converter interface {\n\t// ToGRPCCode converts an HTTP error code into the corresponding gRPC response status.\n\tToGRPCCode(code int) codes.Code\n\n\t// FromGRPCCode converts a gRPC error code into the corresponding HTTP response status.\n\tFromGRPCCode(code codes.Code) int\n}\n\ntype statusConverter struct{}\n\n// DefaultConverter default converter.\nvar DefaultConverter Converter = statusConverter{}\n\n// ToGRPCCode converts an HTTP error code into the corresponding gRPC response status.\n// See: https://github.com/googleapis/googleapis/blob/master/google/rpc/code.proto\nfunc (c statusConverter) ToGRPCCode(code int) codes.Code {\n\tswitch code {\n\tcase http.StatusOK:\n\t\treturn codes.OK\n\tcase http.StatusBadRequest:\n\t\treturn codes.InvalidArgument\n\tcase http.StatusUnauthorized:\n\t\treturn codes.Unauthenticated\n\tcase http.StatusForbidden:\n\t\treturn codes.PermissionDenied\n\tcase http.StatusNotFound:\n\t\treturn codes.NotFound\n\tcase http.StatusConflict:\n\t\treturn codes.Aborted\n\tcase http.StatusTooManyRequests:\n\t\treturn codes.ResourceExhausted\n\tcase http.StatusInternalServerError:\n\t\treturn codes.Internal\n\tcase http.StatusNotImplemented:\n\t\treturn codes.Unimplemented\n\tcase http.StatusServiceUnavailable:\n\t\treturn codes.Unavailable\n\tcase http.StatusGatewayTimeout:\n\t\treturn codes.DeadlineExceeded\n\tcase ClientClosed:\n\t\treturn codes.Canceled\n\tcase http.StatusPreconditionFailed:\n\t\treturn codes.FailedPrecondition\n\t}\n\treturn codes.Unknown\n}\n\n// FromGRPCCode converts a gRPC error code into the corresponding HTTP response status.\n// See: https://github.com/googleapis/googleapis/blob/master/google/rpc/code.proto\nfunc (c statusConverter) FromGRPCCode(code codes.Code) int {\n\tswitch code {\n\tcase codes.OK:\n\t\treturn http.StatusOK\n\tcase codes.Canceled:\n\t\treturn ClientClosed\n\tcase codes.Unknown:\n\t\treturn http.StatusInternalServerError\n\tcase codes.InvalidArgument:\n\t\treturn http.StatusBadRequest\n\tcase codes.DeadlineExceeded:\n\t\treturn http.StatusGatewayTimeout\n\tcase codes.NotFound:\n\t\treturn http.StatusNotFound\n\tcase codes.AlreadyExists:\n\t\treturn http.StatusConflict\n\tcase codes.PermissionDenied:\n\t\treturn http.StatusForbidden\n\tcase codes.Unauthenticated:\n\t\treturn http.StatusUnauthorized\n\tcase codes.ResourceExhausted:\n\t\treturn http.StatusTooManyRequests\n\tcase codes.Aborted:\n\t\treturn http.StatusConflict\n\tcase codes.OutOfRange:\n\t\treturn http.StatusBadRequest\n\tcase codes.Unimplemented:\n\t\treturn http.StatusNotImplemented\n\tcase codes.Internal:\n\t\treturn http.StatusInternalServerError\n\tcase codes.Unavailable:\n\t\treturn http.StatusServiceUnavailable\n\tcase codes.DataLoss:\n\t\treturn http.StatusInternalServerError\n\tcase codes.FailedPrecondition:\n\t\treturn http.StatusPreconditionFailed\n\t}\n\treturn http.StatusInternalServerError\n}\n\n// ToGRPCCode converts an HTTP error code into the corresponding gRPC response status.\nfunc ToGRPCCode(code int) codes.Code {\n\treturn DefaultConverter.ToGRPCCode(code)\n}\n\n// FromGRPCCode converts a gRPC error code into the corresponding HTTP response status.\nfunc FromGRPCCode(code codes.Code) int {\n\treturn DefaultConverter.FromGRPCCode(code)\n}\n"
  },
  {
    "path": "transport/http/status/status_test.go",
    "content": "package status\n\nimport (\n\t\"net/http\"\n\t\"testing\"\n\n\t\"google.golang.org/grpc/codes\"\n)\n\nfunc TestToGRPCCode(t *testing.T) {\n\ttests := []struct {\n\t\tname string\n\t\tcode int\n\t\twant codes.Code\n\t}{\n\t\t{\"http.StatusOK\", http.StatusOK, codes.OK},\n\t\t{\"http.StatusBadRequest\", http.StatusBadRequest, codes.InvalidArgument},\n\t\t{\"http.StatusUnauthorized\", http.StatusUnauthorized, codes.Unauthenticated},\n\t\t{\"http.StatusForbidden\", http.StatusForbidden, codes.PermissionDenied},\n\t\t{\"http.StatusNotFound\", http.StatusNotFound, codes.NotFound},\n\t\t{\"http.StatusConflict\", http.StatusConflict, codes.Aborted},\n\t\t{\"http.StatusTooManyRequests\", http.StatusTooManyRequests, codes.ResourceExhausted},\n\t\t{\"http.StatusInternalServerError\", http.StatusInternalServerError, codes.Internal},\n\t\t{\"http.StatusNotImplemented\", http.StatusNotImplemented, codes.Unimplemented},\n\t\t{\"http.StatusServiceUnavailable\", http.StatusServiceUnavailable, codes.Unavailable},\n\t\t{\"http.StatusGatewayTimeout\", http.StatusGatewayTimeout, codes.DeadlineExceeded},\n\t\t{\"StatusClientClosed\", ClientClosed, codes.Canceled},\n\t\t{\"http.StatusPreconditionFailed\", http.StatusPreconditionFailed, codes.FailedPrecondition},\n\t\t{\"else\", 100000, codes.Unknown},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tif got := ToGRPCCode(tt.code); got != tt.want {\n\t\t\t\tt.Errorf(\"GRPCCodeFromStatus() = %v, want %v\", got, tt.want)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestFromGRPCCode(t *testing.T) {\n\ttests := []struct {\n\t\tname string\n\t\tcode codes.Code\n\t\twant int\n\t}{\n\t\t{\"codes.OK\", codes.OK, http.StatusOK},\n\t\t{\"codes.Canceled\", codes.Canceled, ClientClosed},\n\t\t{\"codes.Unknown\", codes.Unknown, http.StatusInternalServerError},\n\t\t{\"codes.InvalidArgument\", codes.InvalidArgument, http.StatusBadRequest},\n\t\t{\"codes.DeadlineExceeded\", codes.DeadlineExceeded, http.StatusGatewayTimeout},\n\t\t{\"codes.NotFound\", codes.NotFound, http.StatusNotFound},\n\t\t{\"codes.AlreadyExists\", codes.AlreadyExists, http.StatusConflict},\n\t\t{\"codes.PermissionDenied\", codes.PermissionDenied, http.StatusForbidden},\n\t\t{\"codes.Unauthenticated\", codes.Unauthenticated, http.StatusUnauthorized},\n\t\t{\"codes.ResourceExhausted\", codes.ResourceExhausted, http.StatusTooManyRequests},\n\t\t{\"codes.FailedPrecondition\", codes.FailedPrecondition, http.StatusPreconditionFailed},\n\t\t{\"codes.Aborted\", codes.Aborted, http.StatusConflict},\n\t\t{\"codes.OutOfRange\", codes.OutOfRange, http.StatusBadRequest},\n\t\t{\"codes.Unimplemented\", codes.Unimplemented, http.StatusNotImplemented},\n\t\t{\"codes.Internal\", codes.Internal, http.StatusInternalServerError},\n\t\t{\"codes.Unavailable\", codes.Unavailable, http.StatusServiceUnavailable},\n\t\t{\"codes.DataLoss\", codes.DataLoss, http.StatusInternalServerError},\n\t\t{\"else\", codes.Code(10000), http.StatusInternalServerError},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tif got := FromGRPCCode(tt.code); got != tt.want {\n\t\t\t\tt.Errorf(\"StatusFromGRPCCode() = %v, want %v\", got, tt.want)\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "transport/http/transport.go",
    "content": "package http\n\nimport (\n\t\"context\"\n\t\"net/http\"\n\n\t\"github.com/go-kratos/kratos/v2/transport\"\n)\n\nvar _ Transporter = (*Transport)(nil)\n\nvar _ ResponseTransporter = (*Transport)(nil)\n\n// Transporter is http Transporter\ntype Transporter interface {\n\ttransport.Transporter\n\tRequest() *http.Request\n\tPathTemplate() string\n}\n\n// ResponseTransporter extends Transporter with HTTP response access\n// This interface provides access to the http.ResponseWriter for use cases\n// like file downloads, streaming responses, or direct response manipulation.\ntype ResponseTransporter interface {\n\tTransporter\n\tResponse() http.ResponseWriter\n}\n\n// Transport is an HTTP transport.\ntype Transport struct {\n\tendpoint     string\n\toperation    string\n\treqHeader    headerCarrier\n\treplyHeader  headerCarrier\n\trequest      *http.Request\n\tresponse     http.ResponseWriter\n\tpathTemplate string\n}\n\n// Kind returns the transport kind.\nfunc (tr *Transport) Kind() transport.Kind {\n\treturn transport.KindHTTP\n}\n\n// Endpoint returns the transport endpoint.\nfunc (tr *Transport) Endpoint() string {\n\treturn tr.endpoint\n}\n\n// Operation returns the transport operation.\nfunc (tr *Transport) Operation() string {\n\treturn tr.operation\n}\n\n// Request returns the HTTP request.\nfunc (tr *Transport) Request() *http.Request {\n\treturn tr.request\n}\n\n// RequestHeader returns the request header.\nfunc (tr *Transport) RequestHeader() transport.Header {\n\treturn tr.reqHeader\n}\n\n// Response returns the HTTP response.\nfunc (tr *Transport) Response() http.ResponseWriter {\n\treturn tr.response\n}\n\n// ReplyHeader returns the reply header.\nfunc (tr *Transport) ReplyHeader() transport.Header {\n\treturn tr.replyHeader\n}\n\n// PathTemplate returns the http path template.\nfunc (tr *Transport) PathTemplate() string {\n\treturn tr.pathTemplate\n}\n\n// SetOperation sets the transport operation.\nfunc SetOperation(ctx context.Context, op string) {\n\tif tr, ok := transport.FromServerContext(ctx); ok {\n\t\tif tr, ok := tr.(*Transport); ok {\n\t\t\ttr.operation = op\n\t\t}\n\t}\n}\n\n// SetCookie adds a Set-Cookie header to the provided [ResponseWriter]'s headers.\n// The provided cookie must have a valid Name. Invalid cookies may be\n// silently dropped.\nfunc SetCookie(ctx context.Context, cookie *http.Cookie) {\n\tif tr, ok := transport.FromServerContext(ctx); ok {\n\t\tif tr, ok := tr.(*Transport); ok {\n\t\t\thttp.SetCookie(tr.response, cookie)\n\t\t}\n\t}\n}\n\n// RequestFromServerContext returns request from context.\nfunc RequestFromServerContext(ctx context.Context) (*http.Request, bool) {\n\tif tr, ok := transport.FromServerContext(ctx); ok {\n\t\tif htr, ok := tr.(Transporter); ok {\n\t\t\treturn htr.Request(), true\n\t\t}\n\t}\n\treturn nil, false\n}\n\ntype headerCarrier http.Header\n\n// Get returns the value associated with the passed key.\nfunc (hc headerCarrier) Get(key string) string {\n\treturn http.Header(hc).Get(key)\n}\n\n// Set stores the key-value pair.\nfunc (hc headerCarrier) Set(key string, value string) {\n\thttp.Header(hc).Set(key, value)\n}\n\n// Add append value to key-values pair.\nfunc (hc headerCarrier) Add(key string, value string) {\n\thttp.Header(hc).Add(key, value)\n}\n\n// Keys lists the keys stored in this carrier.\nfunc (hc headerCarrier) Keys() []string {\n\tkeys := make([]string, 0, len(hc))\n\tfor k := range http.Header(hc) {\n\t\tkeys = append(keys, k)\n\t}\n\treturn keys\n}\n\n// Values returns a slice of values associated with the passed key.\nfunc (hc headerCarrier) Values(key string) []string {\n\treturn http.Header(hc).Values(key)\n}\n\n// ResponseWriterFromServerContext returns the http.ResponseWriter from context if available.\n// This function provides backward compatibility and safe access to the ResponseWriter.\n// Returns nil if the transport doesn't implement ResponseTransporter.\nfunc ResponseWriterFromServerContext(ctx context.Context) (http.ResponseWriter, bool) {\n\tif tr, ok := transport.FromServerContext(ctx); ok {\n\t\tif httpTr, ok := tr.(ResponseTransporter); ok {\n\t\t\treturn httpTr.Response(), true\n\t\t}\n\t}\n\treturn nil, false\n}\n"
  },
  {
    "path": "transport/http/transport_test.go",
    "content": "package http\n\nimport (\n\t\"context\"\n\t\"net/http\"\n\t\"reflect\"\n\t\"sort\"\n\t\"testing\"\n\n\t\"github.com/go-kratos/kratos/v2/transport\"\n)\n\nfunc TestTransport_Kind(t *testing.T) {\n\to := &Transport{}\n\tif !reflect.DeepEqual(transport.KindHTTP, o.Kind()) {\n\t\tt.Errorf(\"expect %v, got %v\", transport.KindHTTP, o.Kind())\n\t}\n}\n\nfunc TestTransport_Endpoint(t *testing.T) {\n\tv := \"hello\"\n\to := &Transport{endpoint: v}\n\tif !reflect.DeepEqual(v, o.Endpoint()) {\n\t\tt.Errorf(\"expect %v, got %v\", v, o.Endpoint())\n\t}\n}\n\nfunc TestTransport_Operation(t *testing.T) {\n\tv := \"hello\"\n\to := &Transport{operation: v}\n\tif !reflect.DeepEqual(v, o.Operation()) {\n\t\tt.Errorf(\"expect %v, got %v\", v, o.Operation())\n\t}\n}\n\nfunc TestTransport_Request(t *testing.T) {\n\tv := &http.Request{}\n\to := &Transport{request: v}\n\tif !reflect.DeepEqual(v, o.Request()) {\n\t\tt.Errorf(\"expect %v, got %v\", v, o.Request())\n\t}\n}\n\nfunc TestTransport_RequestHeader(t *testing.T) {\n\tv := headerCarrier{}\n\tv.Set(\"a\", \"1\")\n\to := &Transport{reqHeader: v}\n\tif !reflect.DeepEqual(\"1\", o.RequestHeader().Get(\"a\")) {\n\t\tt.Errorf(\"expect %v, got %v\", \"1\", o.RequestHeader().Get(\"a\"))\n\t}\n}\n\nfunc TestTransport_Response(t *testing.T) {\n\tv := http.ResponseWriter(nil)\n\to := &Transport{response: v}\n\tif !reflect.DeepEqual(v, o.Response()) {\n\t\tt.Errorf(\"expect %v, got %v\", v, o.Response())\n\t}\n}\n\nfunc TestTransport_ReplyHeader(t *testing.T) {\n\tv := headerCarrier{}\n\tv.Set(\"a\", \"1\")\n\to := &Transport{replyHeader: v}\n\tif !reflect.DeepEqual(\"1\", o.ReplyHeader().Get(\"a\")) {\n\t\tt.Errorf(\"expect %v, got %v\", \"1\", o.ReplyHeader().Get(\"a\"))\n\t}\n}\n\nfunc TestTransport_PathTemplate(t *testing.T) {\n\tv := \"template\"\n\to := &Transport{pathTemplate: v}\n\tif !reflect.DeepEqual(v, o.PathTemplate()) {\n\t\tt.Errorf(\"expect %v, got %v\", v, o.PathTemplate())\n\t}\n}\n\nfunc TestHeaderCarrier_Keys(t *testing.T) {\n\tv := headerCarrier{}\n\tv.Set(\"abb\", \"1\")\n\tv.Set(\"bcc\", \"2\")\n\twant := []string{\"Abb\", \"Bcc\"}\n\tkeys := v.Keys()\n\tsort.Slice(want, func(i, j int) bool {\n\t\treturn want[i] < want[j]\n\t})\n\tsort.Slice(keys, func(i, j int) bool {\n\t\treturn keys[i] < keys[j]\n\t})\n\tif !reflect.DeepEqual(want, keys) {\n\t\tt.Errorf(\"expect %v, got %v\", want, keys)\n\t}\n}\n\nfunc TestSetOperation(t *testing.T) {\n\ttr := &Transport{}\n\tctx := transport.NewServerContext(context.Background(), tr)\n\tSetOperation(ctx, \"kratos\")\n\tif !reflect.DeepEqual(tr.operation, \"kratos\") {\n\t\tt.Errorf(\"expect %v, got %v\", \"kratos\", tr.operation)\n\t}\n}\n\n// TestResponseTransporter_Interface tests that Transport implements ResponseTransporter\nfunc TestResponseTransporter_Interface(t *testing.T) {\n\tvar transport Transporter = &Transport{}\n\tif _, ok := transport.(ResponseTransporter); !ok {\n\t\tt.Error(\"Transport should implement ResponseTransporter interface\")\n\t}\n}\n\n// TestResponseWriterFromServerContext tests the ResponseWriterFromServerContext helper function\nfunc TestResponseWriterFromServerContext(t *testing.T) {\n\ttests := []struct {\n\t\tname         string\n\t\tsetupContext func() context.Context\n\t\texpectWriter bool\n\t\texpectOk     bool\n\t}{\n\t\t{\n\t\t\tname: \"valid HTTP transport with ResponseWriter\",\n\t\t\tsetupContext: func() context.Context {\n\t\t\t\tmockWriter := &mockResponseWriter{header: make(http.Header)}\n\t\t\t\ttr := &Transport{response: mockWriter}\n\t\t\t\treturn transport.NewServerContext(context.Background(), tr)\n\t\t\t},\n\t\t\texpectWriter: true,\n\t\t\texpectOk:     true,\n\t\t},\n\t\t{\n\t\t\tname: \"context without transport\",\n\t\t\tsetupContext: func() context.Context {\n\t\t\t\treturn context.Background()\n\t\t\t},\n\t\t\texpectWriter: false,\n\t\t\texpectOk:     false,\n\t\t},\n\t\t{\n\t\t\tname: \"context with non-HTTP transport\",\n\t\t\tsetupContext: func() context.Context {\n\t\t\t\ttr := &mockNonHTTPTransport{}\n\t\t\t\treturn transport.NewServerContext(context.Background(), tr)\n\t\t\t},\n\t\t\texpectWriter: false,\n\t\t\texpectOk:     false,\n\t\t},\n\t\t{\n\t\t\tname: \"context with HTTP transport without ResponseTransporter interface\",\n\t\t\tsetupContext: func() context.Context {\n\t\t\t\ttr := &mockBasicHTTPTransport{}\n\t\t\t\treturn transport.NewServerContext(context.Background(), tr)\n\t\t\t},\n\t\t\texpectWriter: false,\n\t\t\texpectOk:     false,\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tctx := tt.setupContext()\n\t\t\twriter, ok := ResponseWriterFromServerContext(ctx)\n\n\t\t\tif ok != tt.expectOk {\n\t\t\t\tt.Errorf(\"ResponseWriterFromServerContext() ok = %v, want %v\", ok, tt.expectOk)\n\t\t\t}\n\n\t\t\tif tt.expectWriter && writer == nil {\n\t\t\t\tt.Error(\"ResponseWriterFromServerContext() should return non-nil writer\")\n\t\t\t}\n\n\t\t\tif !tt.expectWriter && writer != nil {\n\t\t\t\tt.Error(\"ResponseWriterFromServerContext() should return nil writer\")\n\t\t\t}\n\t\t})\n\t}\n}\n\n// TestTransport_InterfaceCompatibility tests interface compatibility\nfunc TestTransport_InterfaceCompatibility(t *testing.T) {\n\ttr := &Transport{\n\t\tendpoint:     \"http://localhost:8080\",\n\t\toperation:    \"/test\",\n\t\tpathTemplate: \"/test/{id}\",\n\t\trequest:      &http.Request{},\n\t\tresponse:     &mockResponseWriter{header: make(http.Header)},\n\t}\n\n\t// Test basic Transporter interface\n\tvar basicTransporter Transporter = tr\n\tif basicTransporter.Request() == nil {\n\t\tt.Error(\"Transporter.Request() should not be nil\")\n\t}\n\tif basicTransporter.PathTemplate() == \"\" {\n\t\tt.Error(\"Transporter.PathTemplate() should not be empty\")\n\t}\n\n\t// Test ResponseTransporter interface\n\tvar responseTransporter ResponseTransporter = tr\n\tif responseTransporter.Response() == nil {\n\t\tt.Error(\"ResponseTransporter.Response() should not be nil\")\n\t}\n\n\t// Test that ResponseTransporter extends Transporter\n\tif responseTransporter.Request() == nil {\n\t\tt.Error(\"ResponseTransporter should have Request() method from Transporter\")\n\t}\n}\n\n// TestTransport_TypeAssertion tests safe type assertion patterns\nfunc TestTransport_TypeAssertion(t *testing.T) {\n\ttr := &Transport{\n\t\tresponse: &mockResponseWriter{header: make(http.Header)},\n\t}\n\tctx := transport.NewServerContext(context.Background(), tr)\n\n\t// Test type assertion to ResponseTransporter\n\tif serverTr, ok := transport.FromServerContext(ctx); ok {\n\t\tif httpTr, ok := serverTr.(ResponseTransporter); ok {\n\t\t\tif httpTr.Response() == nil {\n\t\t\t\tt.Error(\"ResponseTransporter.Response() should not be nil\")\n\t\t\t}\n\t\t} else {\n\t\t\tt.Error(\"Transport should be assertable to ResponseTransporter\")\n\t\t}\n\t} else {\n\t\tt.Error(\"Should be able to get transport from server context\")\n\t}\n}\n\n// Mock implementations for testing\n// Note: mockResponseWriter is already defined in codec_test.go and will be reused\n\n// mockNonHTTPTransport simulates a non-HTTP transport (like gRPC)\ntype mockNonHTTPTransport struct{}\n\nfunc (m *mockNonHTTPTransport) Kind() transport.Kind            { return transport.KindGRPC }\nfunc (m *mockNonHTTPTransport) Endpoint() string                { return \"grpc://localhost:9000\" }\nfunc (m *mockNonHTTPTransport) Operation() string               { return \"/grpc.Service/Method\" }\nfunc (m *mockNonHTTPTransport) RequestHeader() transport.Header { return nil }\nfunc (m *mockNonHTTPTransport) ReplyHeader() transport.Header   { return nil }\n\n// mockBasicHTTPTransport simulates an HTTP transport that only implements basic Transporter\ntype mockBasicHTTPTransport struct{}\n\nfunc (m *mockBasicHTTPTransport) Kind() transport.Kind            { return transport.KindHTTP }\nfunc (m *mockBasicHTTPTransport) Endpoint() string                { return \"http://localhost:8080\" }\nfunc (m *mockBasicHTTPTransport) Operation() string               { return \"/test\" }\nfunc (m *mockBasicHTTPTransport) RequestHeader() transport.Header { return nil }\nfunc (m *mockBasicHTTPTransport) ReplyHeader() transport.Header   { return nil }\nfunc (m *mockBasicHTTPTransport) Request() *http.Request          { return nil }\nfunc (m *mockBasicHTTPTransport) PathTemplate() string            { return \"/test\" }\n"
  },
  {
    "path": "transport/transport.go",
    "content": "package transport\n\nimport (\n\t\"context\"\n\t\"net/url\"\n\n\t// init encoding\n\t_ \"github.com/go-kratos/kratos/v2/encoding/form\"\n\t_ \"github.com/go-kratos/kratos/v2/encoding/json\"\n\t_ \"github.com/go-kratos/kratos/v2/encoding/proto\"\n\t_ \"github.com/go-kratos/kratos/v2/encoding/xml\"\n\t_ \"github.com/go-kratos/kratos/v2/encoding/yaml\"\n)\n\n// Server is transport server.\ntype Server interface {\n\tStart(context.Context) error\n\tStop(context.Context) error\n}\n\n// Endpointer is registry endpoint.\ntype Endpointer interface {\n\tEndpoint() (*url.URL, error)\n}\n\n// Header is the storage medium used by a Header.\ntype Header interface {\n\tGet(key string) string\n\tSet(key string, value string)\n\tAdd(key string, value string)\n\tKeys() []string\n\tValues(key string) []string\n}\n\n// Transporter is transport context value interface.\ntype Transporter interface {\n\t// Kind transporter\n\t// grpc\n\t// http\n\tKind() Kind\n\t// Endpoint return server or client endpoint\n\t// Server Transport: grpc://127.0.0.1:9000\n\t// Client Transport: discovery:///provider-demo\n\tEndpoint() string\n\t// Operation Service full method selector generated by protobuf\n\t// example: /helloworld.Greeter/SayHello\n\tOperation() string\n\t// RequestHeader return transport request header\n\t// http: http.Header\n\t// grpc: metadata.MD\n\tRequestHeader() Header\n\t// ReplyHeader return transport reply/response header\n\t// only valid for server transport\n\t// http: http.Header\n\t// grpc: metadata.MD\n\tReplyHeader() Header\n}\n\n// Kind defines the type of Transport\ntype Kind string\n\nfunc (k Kind) String() string { return string(k) }\n\n// Defines a set of transport kind\nconst (\n\tKindGRPC Kind = \"grpc\"\n\tKindHTTP Kind = \"http\"\n)\n\ntype (\n\tserverTransportKey struct{}\n\tclientTransportKey struct{}\n)\n\n// NewServerContext returns a new Context that carries value.\nfunc NewServerContext(ctx context.Context, tr Transporter) context.Context {\n\treturn context.WithValue(ctx, serverTransportKey{}, tr)\n}\n\n// FromServerContext returns the Transport value stored in ctx, if any.\nfunc FromServerContext(ctx context.Context) (tr Transporter, ok bool) {\n\ttr, ok = ctx.Value(serverTransportKey{}).(Transporter)\n\treturn\n}\n\n// NewClientContext returns a new Context that carries value.\nfunc NewClientContext(ctx context.Context, tr Transporter) context.Context {\n\treturn context.WithValue(ctx, clientTransportKey{}, tr)\n}\n\n// FromClientContext returns the Transport value stored in ctx, if any.\nfunc FromClientContext(ctx context.Context) (tr Transporter, ok bool) {\n\ttr, ok = ctx.Value(clientTransportKey{}).(Transporter)\n\treturn\n}\n"
  },
  {
    "path": "transport/transport_test.go",
    "content": "package transport\n\nimport (\n\t\"context\"\n\t\"reflect\"\n\t\"testing\"\n)\n\n// mockTransport is a gRPC transport.\ntype mockTransport struct {\n\tendpoint  string\n\toperation string\n}\n\n// Kind returns the transport kind.\nfunc (tr *mockTransport) Kind() Kind {\n\treturn KindGRPC\n}\n\n// Endpoint returns the transport endpoint.\nfunc (tr *mockTransport) Endpoint() string {\n\treturn tr.endpoint\n}\n\n// Operation returns the transport operation.\nfunc (tr *mockTransport) Operation() string {\n\treturn tr.operation\n}\n\n// RequestHeader returns the request header.\nfunc (tr *mockTransport) RequestHeader() Header {\n\treturn nil\n}\n\n// ReplyHeader returns the reply header.\nfunc (tr *mockTransport) ReplyHeader() Header {\n\treturn nil\n}\n\nfunc TestServerTransport(t *testing.T) {\n\tctx := context.Background()\n\n\tctx = NewServerContext(ctx, &mockTransport{endpoint: \"test_endpoint\"})\n\ttr, ok := FromServerContext(ctx)\n\tif !ok {\n\t\tt.Errorf(\"expected:%v got:%v\", true, ok)\n\t}\n\tif tr == nil {\n\t\tt.Errorf(\"expected:%v got:%v\", nil, tr)\n\t}\n\tmtr, ok := tr.(*mockTransport)\n\tif !ok {\n\t\tt.Errorf(\"expected:%v got:%v\", true, ok)\n\t}\n\tif mtr == nil {\n\t\tt.Fatalf(\"expected:%v got:%v\", nil, mtr)\n\t}\n\tif mtr.Kind().String() != KindGRPC.String() {\n\t\tt.Errorf(\"expected:%v got:%v\", KindGRPC.String(), mtr.Kind().String())\n\t}\n\tif !reflect.DeepEqual(mtr.endpoint, \"test_endpoint\") {\n\t\tt.Errorf(\"expected:%v got:%v\", \"test_endpoint\", mtr.endpoint)\n\t}\n}\n\nfunc TestClientTransport(t *testing.T) {\n\tctx := context.Background()\n\n\tctx = NewClientContext(ctx, &mockTransport{endpoint: \"test_endpoint\"})\n\ttr, ok := FromClientContext(ctx)\n\tif !ok {\n\t\tt.Errorf(\"expected:%v got:%v\", true, ok)\n\t}\n\tif tr == nil {\n\t\tt.Errorf(\"expected:%v got:%v\", nil, tr)\n\t}\n\tmtr, ok := tr.(*mockTransport)\n\tif !ok {\n\t\tt.Errorf(\"expected:%v got:%v\", true, ok)\n\t}\n\tif mtr == nil {\n\t\tt.Errorf(\"expected:%v got:%v\", nil, mtr)\n\t}\n\tif !reflect.DeepEqual(mtr.endpoint, \"test_endpoint\") {\n\t\tt.Errorf(\"expected:%v got:%v\", \"test_endpoint\", mtr.endpoint)\n\t}\n}\n"
  },
  {
    "path": "version.go",
    "content": "package kratos\n\n// Release is the current kratos version.\nconst Release = \"v2.9.2\"\n"
  }
]