[
  {
    "path": ".codecov.yml",
    "content": "ignore:\n  - \"docs/ex/**/*.go\"\n  - \"internal/e2e/**/*.go\"\n\ncoverage:\n  range: 80..100\n  round: down\n  precision: 2\n\n  status:\n    project:                   # measuring the overall project coverage\n      default:                 # context, you can create multiple ones with custom titles\n        enabled: yes           # must be yes|true to enable this status\n        target: 90%             # specify the target coverage for each commit status\n                               #   option: \"auto\" (must increase from parent commit or pull request base)\n                               #   option: \"X%\" a static target percentage to hit\n        if_not_found: success  # if parent is not found report status as success, error, or failure\n        if_ci_failed: error    # if ci fails report status as success, error, or failure\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.md",
    "content": "---\nname: Bug report\nabout: Create a report to help us improve\ntitle: ''\nlabels: ''\nassignees: ''\n\n---\n\n**Describe the bug**\nA clear and concise description of what the bug is.\n\n**To Reproduce**\nSteps to reproduce the behavior\n\n**Expected behavior**\nA clear and concise description of what you expected to happen.\n\n**Additional context**\nAdd any other context about the problem here.\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/config.yml",
    "content": "contact_links:\n  - name: Questions\n    about: Please use our Discussions page\n    url: https://github.com/uber-go/fx/discussions\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature_request.md",
    "content": "---\nname: Feature request\nabout: Suggest an idea for this project\ntitle: ''\nlabels: ''\nassignees: ''\n\n---\n\n**Is your feature request related to a problem? Please describe.**\n\n**Describe the solution you'd like**\nA clear and concise description of what you want to happen.\n\n**Describe alternatives you've considered**\nA clear and concise description of any alternative solutions or features you've considered.\n\n**Is this a breaking change?**\nWe do not accept breaking changes to the existing API. Please consider if your proposed solution is backwards compatible. If not, we can help you make it backwards compatible, but this must be considered when we consider new features.\n\n**Additional context**\nAdd any other context or screenshots about the feature request here.\n"
  },
  {
    "path": ".github/dependabot.yml",
    "content": "version: 2\nupdates:\n  - package-ecosystem: \"github-actions\"\n    directory: \"/\"\n    schedule:\n      interval: \"weekly\"\n\n  # Auto-update tools dependencies, but not library dependencies.\n  - package-ecosystem: \"gomod\"\n    directory: \"/tools\"\n    schedule:\n      interval: \"weekly\"\n"
  },
  {
    "path": ".github/workflows/docs.yml",
    "content": "name: GitHub Pages\n\non:\n  push:\n    tags: ['v*']\n\n  # This lets us publish the workflow\n  # manually from the GitHub Actions UI.\n  #\n  # It expects a single input:\n  # the Git ref we want to build and publish the docs for.\n  workflow_dispatch:\n    inputs:\n      head:\n        description: \"Git commitish to check out.\"\n        required: true\n        type: string\n\n\n# Run at most one publish job at a time,\n# cancelling others if a new one starts.\nconcurrency:\n  group: \"pages\"\n  cancel-in-progress: true\n\n\nenv:\n  UV_VERSION: 0.3.3\n\njobs:\n  build:\n    runs-on: ubuntu-latest\n\n    steps:\n      # We have two checkout steps running based on\n      # whether the workflow was manually triggered.\n      # If manually triggered, we use the provided ref,\n      # otherwise we use the default ref.\n      - name: Checkout (on release)\n        if:  github.event_name != 'workflow_dispatch'\n        uses: actions/checkout@v4\n      - name: Checkout (manual)\n        if:  github.event_name == 'workflow_dispatch'\n        uses: actions/checkout@v4\n        with:\n          ref: ${{ inputs.head }}\n\n      - name: Install uv\n        run: |\n          curl -LsSf \"https://astral.sh/uv/${UV_VERSION}/install.sh\" | sh\n\n      - name: Build\n        run: make docs\n\n      - name: Upload artifact\n        uses: actions/upload-pages-artifact@v3\n        with:\n          path: docs/_site\n\n  deploy:\n    needs: build  # run only after a successful build\n\n    permissions:\n      pages: write      # to deploy to Pages\n      id-token: write   # to verify the deployment originates from an appropriate source\n\n    environment:\n      name: github-pages\n      url: ${{ steps.deployment.outputs.page_url }}\n\n    runs-on: ubuntu-latest\n    steps:\n      - name: Deploy to GitHub Pages\n        id: deployment\n        uses: actions/deploy-pages@v4\n"
  },
  {
    "path": ".github/workflows/fossa.yaml",
    "content": "name: FOSSA Analysis\non: push\n\njobs:\n\n  build:\n    runs-on: ubuntu-latest\n    if: github.repository_owner == 'uber-go'\n    steps:\n      - name: Checkout code\n        uses: actions/checkout@v4\n\n      - name: FOSSA analysis\n        uses: fossas/fossa-action@v1\n        with:\n          api-key: ${{ secrets.FOSSA_API_KEY }}\n\npermissions:\n  contents: read\n"
  },
  {
    "path": ".github/workflows/go.yml",
    "content": "name: Go\n\non:\n  push:\n    branches: ['*']\n    tags: ['v*']\n  pull_request:\n    branches: ['*']\n\npermissions:\n  contents: read\n\njobs:\n\n  build:\n    runs-on: ${{ matrix.os }}\n    name: Build and test\n\n    strategy:\n      matrix:\n        os: [\"ubuntu-latest\", \"windows-latest\"]\n        go: [\"1.24.x\", \"1.25.x\"]\n\n    steps:\n    - name: Checkout code\n      uses: actions/checkout@v4\n\n    - name: Setup Go\n      uses: actions/setup-go@v5\n      with:\n        go-version: ${{ matrix.go }}\n\n    - name: Download Dependencies\n      run: go mod download\n\n    - name: Test\n      run: make cover\n\n    - name: Upload coverage to codecov.io\n      uses: codecov/codecov-action@v4\n      env:\n        CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}\n\n  doctest:\n    runs-on: ubuntu-latest\n    name: Test documentation\n\n    steps:\n    - name: Checkout code\n      uses: actions/checkout@v4\n\n    - name: Setup Go\n      uses: actions/setup-go@v5\n      with:\n        go-version: 1.25.x\n\n    - name: Test\n      run: make cover COVER_MODULES=./docs\n\n  lint:\n    name: Lint\n    runs-on: ubuntu-latest\n\n    steps:\n    - name: Checkout code\n      uses: actions/checkout@v4\n\n    - name: Setup Go\n      uses: actions/setup-go@v5\n      with:\n        go-version: 1.25.x\n\n    - uses: golangci/golangci-lint-action@v8\n      name: Install golangci-lint\n      with:\n        version: latest\n        args: --help  # make lint will run the linter\n\n    - run: make lint\n      name: Lint\n"
  },
  {
    "path": ".gitignore",
    "content": "/vendor\n/.bench\n*.mem\n*.cpu\n*.test\n*.log\n*.out\n*.html\n*.coverprofile\ncoverage.txt\n*.pprof\n/.bin\n/.cache\n/bin\n.vscode\n.mdoxcache\n"
  },
  {
    "path": ".golangci.yml",
    "content": "version: \"2\"\n\nissues:\n  # Print all issues reported by all linters.\n  max-issues-per-linter: 0\n  max-same-issues: 0\n\nlinters:\n  # We'll track the golangci-lint default linters manually\n  # instead of letting them change without our control.\n  default: none\n  enable:\n    # golangci-lint defaults:\n    - govet\n    - ineffassign\n    - staticcheck\n    - unused\n\n    # Our own extras:\n    - errorlint\n    - nolintlint  # lints //nolint directives\n    - revive\n\n    # License header check:\n    - goheader\n\n  settings:\n\n    # These govet checks are disabled by default, but they're useful.\n    govet:\n      enable:\n        - nilness\n        - reflectvaluecompare\n        - sortslice\n        - unusedwrite\n\n    goheader:\n      values:\n        const:\n          COMPANY: Uber Technologies, Inc.\n        regexp:\n          YEAR_RANGE: \\d{4}(-\\d{4})?\n      template: |-\n        Copyright (c) {{ YEAR_RANGE }} {{ COMPANY }}\n\n        Permission is hereby granted, free of charge, to any person obtaining a copy\n        of this software and associated documentation files (the \"Software\"), to deal\n        in the Software without restriction, including without limitation the rights\n        to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n        copies of the Software, and to permit persons to whom the Software is\n        furnished to do so, subject to the following conditions:\n\n        The above copyright notice and this permission notice shall be included in\n        all copies or substantial portions of the Software.\n\n        THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n        IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n        FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n        AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n        LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n        OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n        THE SOFTWARE.\n\n  exclusions:\n    generated: lax\n    rules:\n      # Don't warn on unused parameters.\n      # Parameter names are useful; replacing them with '_' is undesirable.\n      - linters: [revive]\n        text: 'unused-parameter: parameter \\S+ seems to be unused, consider removing or renaming it as _'\n\n      # staticcheck already has smarter checks for empty blocks.\n      # revive's empty-block linter has false positives.\n      # For example, as of writing this, the following is not allowed.\n      #   for foo() { }\n      - linters: [revive]\n        text: 'empty-block: this block is empty, you can remove it'\n\n      # It's okay if internal packages and examples in docs/\n      # don't have package comments.\n      - linters: [revive]\n        path: .+/internal/.+|^internal/.+|^docs/.+\n        text: should have a package comment\n\n      # It's okay for tests to use dot imports.\n      - linters: [revive]\n        path: _test\\.go$\n        text: should not use dot imports\n\nformatters:\n  enable: [gofumpt]\n  exclusions:\n    generated: lax\n"
  },
  {
    "path": "CHANGELOG.md",
    "content": "---\nsearch:\n  exclude: true\n---\n\n# Changelog\n\nAll notable changes to this project will be documented in this file.\n\nThe format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)\nand this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).\n\n## Unreleased\n- No changes yet.\n\n## [1.24.0](https://github.com/uber-go/fx/compare/v1.23.0...v1.24.0) - 2025-05-13\n\n### Added\n- A new event `fxevent.BeforeRun` is now emitted before Fx runs a constructor, \n  decorator, or supply/replace stub.\n\n### Changed\n- Clearer error messages are now used when annotation building fails.\n\n## [1.23.0](https://github.com/uber-go/fx/compare/v1.22.2...v1.22.3) - 2024-10-11\n\n### Added\n- Added `Runtime` to `fxevent.Run` event, which stores the runtime of\n  a constructor or a decorator that's run, including functions created\n  by `fx.Supply` and `fx.Replace`.\n\n### Changed\n- Overhauled the documentation website. (https://uber-go.github.io/fx/)\n\n## [1.22.2](https://github.com/uber-go/fx/compare/v1.22.1...v1.22.2) - 2024-08-07\n\n### Fixed\n- A deadlock with the relayer in signal receivers.\n\n### Changed\n- Upgrade Dig dependency to v1.18.0\n\n## [1.22.1](https://github.com/uber-go/fx/compare/v1.22.0...v1.22.1) - 2024-06-25\n\n### Fixed\n- Fx apps will only listen to signals when `.Run()`, `.Wait()`, or `.Done()`\n  are called, fixing a regression introduced in v1.19.0.\n\n## [1.22.0](https://github.com/uber-go/fx/compare/v1.21.1...v1.22.0) - 2024-05-30\n\n### Added\n- Add `fx.Self` which can be passed to the `fx.As` annotation to signify\n  that a type should be provided as itself.\n- Add `fxtest.EnforceTimeout` that can be passed to `fxtest.NewLifecycle`\n  to force `Start` and `Stop` to return context errors when hook context expires.\n\n### Changed\n- `fx.Private` can now be used with `fx.Supply`.\n\n### Fixed\n- Fx apps will no longer listen to OS signals when they are stopped,\n  solving blocking issues in programs that depended on OS signals\n  after an Fx app stops.\n\n## [1.21.1](https://github.com/uber-go/fx/compare/v1.21.0...v1.21.1) - 2024-04-24\n\n### Changed\n- Register Fx provides (e.g. fx.Lifecycle, fx.Shutdowner, fx.DotGraph) before\n  user provides, to increase likelihood of successful custom logger creation.\n\n## [1.21.0](https://github.com/uber-go/fx/compare/v1.20.1...v1.21.0) - 2024-03-13\n\n### Added\n- fxtest: Add WithTestLogger option that uses a `testing.TB` as the\n  Fx event logger.\n- An fxevent logger that can log events using a slog logger has been added.\n\n### Changed\n- Upgrade Dig dependency to v1.17.1\n\n## [1.20.1](https://github.com/uber-go/fx/compare/v1.20.0...v1.20.1) - 2023-10-17\n\n### Added\n- Provided, Decorated, Supplied, and Replaced events now include a trace\n  of module locations through which the option was given to the App.\n- wasi support.\n\n## [1.20.0](https://github.com/uber-go/fx/compare/v1.19.3...v1.20.0) - 2023-06-12\n\n### Added\n- A new event `fxevent.Run` is now emitted when Fx runs a constructor, decorator,\n  or supply/replace stub.\n\n### Changed\n- `fx.Populate` now works with `fx.Annotate`.\n- Upgrade Dig dependency to v1.17.0.\n\n## [1.19.3](https://github.com/uber-go/fx/compare/v1.19.2...v1.19.3) - 2023-04-17\n\n### Changed\n- Fixed several typos in docs.\n- WASM build support.\n- Annotating In and Out structs with From/As annotations generated invalid results.\n  The annotation check now blocks this.\n- `Shutdown`: Support calling from `Invoke`.\n\n### Deprecated\n- Deprecate `ShutdownTimeout` option.\n\n### Fixed\n- Respect Shutdowner ExitCode from calling `Run`.\n\n## [1.19.2](https://github.com/uber-go/fx/compare/v1.19.1...v1.19.2) - 2023-02-21\n\n### Changed\n- Update Dig dependency to v1.16.1.\n\n## [1.19.1](https://github.com/uber-go/fx/compare/v1.19.0...v1.19.1) - 2023-01-10\n\n### Changed\n- Calling `fx.Stop()` after the `App` has already stopped no longer errors out.\n\n### Fixed\n- Addressed a regression in 1.19.0 release which caused apps to ignore OS signals\n  after running for startTimeout duration.\n\n## [1.19.0](https://github.com/uber-go/fx/compare/v1.18.2...v1.19.0) - 2023-01-03\n\n### Added\n- `fx.RecoverFromPanics` Option which allows Fx to recover from user-provided constructors\n  and invoked functions.\n- `fx.Private` that allows the constructor to limit the scope of its outputs to the wrapping\n  `fx.Module`.\n- `ExitCode` ShutdownOption which allows setting custom exit code at the end of app\n  lifecycle.\n- `Wait` which returns a channel that can be used for waiting on application shutdown.\n- fxevent/ZapLogger now exposes `UseLogLevel` and `UseErrorLevel` methods to set\n  the level of the Zap logs produced by it.\n- Add lifecycle hook-convertible methods: `StartHook`, `StopHook`, `StartStopHook`\n  that can be used with more function signatures.\n\n### Changed\n- `fx.WithLogger` can now be passed at `fx.Module` level, setting custom logger at\n  `Module` scope instead of the whole `App`.\n\n### Fixed\n- `fx.OnStart` and `fx.OnStop` Annotations now work with annotated types that was\n  provided by the annotated constructor.\n- fxevent/ZapLogger: Errors from `fx.Supply` are now logged at `Error` level, not\n  `Info`.\n- A race condition in lifecycle Start/Stop methods.\n- Typos in docs.\n\n## [1.18.2](https://github.com/uber-go/fx/compare/v1.18.1...v1.18.2) - 2022-09-28\n\n### Added\n- Clarify ordering of `Invoke`s in `Module`s.\n\n### Fixed\n- Fix `Decorate` not being applied to transitive dependencies at root `App` level.\n\n## [1.18.1](https://github.com/uber-go/fx/compare/v1.18.0...v1.18.1) - 2022-08-08\n\n### Fixed\n- Fix a nil panic when `nil` is passed to `OnStart` and `OnStop` lifecycle methods.\n\n## [1.18.0](https://github.com/uber-go/fx/compare/v1.17.1...v1.18.0) - 2022-08-05\n\n### Added\n- Soft value groups that lets you specify value groups as best-effort dependencies.\n- `fx.OnStart` and `fx.OnStop` annotations which lets you annotate dependencies to provide\n  OnStart and OnStop lifecycle hooks.\n- A new `fxevent.Replaced` event written to `fxevent.Logger` following an `fx.Replace`.\n\n### Fixed\n- Upgrade Dig dependency to v1.14.1 to address a couple of issues with decorations. Refer to\n  Dig v1.14.1 release notes for more details.\n- `fx.WithLogger` no longer ignores decorations and replacements of types that\n  it depends on.\n- Don't run lifecycle hooks if the context for them has already expired.\n- `App.Start` and `App.Stop` no longer deadlock if the OnStart/OnStop hook\n  exits the current goroutine.\n- `fxevent.ConsoleLogger` no longer emits an extraneous argument for the\n  Supplied event.\n\n### Deprecated\n- `fx.Extract` in favor of `fx.Populate`.\n\n## [1.17.1](https://github.com/uber-go/fx/compare/v1.17.0...v1.17.1) - 2022-03-23\n\n### Added\n- Logging for provide/invoke/decorate now includes the associated `fx.Module` name.\n\n## [1.17.0](https://github.com/uber-go/fx/compare/v1.16.0...v1.17.0) - 2022-02-28\n\n### Added\n- Add `fx.Module` which scopes any modifications made to the dependency graph.\n- Add `fx.Decorate` and `fx.Replace` that lets you modify a dependency graph with decorators.\n- Add `fxevent.Decorated` event which gets emitted upon a dependency getting decorated.\n\n### Changed\n- `fx.Annotate`: Validate that `fx.In` or `fx.Out` structs are not passed to it.\n- `fx.Annotate`: Upon failure to Provide, the error contains the actual location\n  of the provided constructor.\n\n## [1.16.0](https://github.com/uber-go/fx/compare/v1.15.0...v1.16.0) - 2021-12-02\n\n### Added\n- Add the ability to provide a function as multiple interfaces at once using `fx.As`.\n\n### Changed\n- `fx.Annotate`: support variadic functions, and feeding value groups into them.\n\n### Fixed\n- Fix an issue where OnStop hooks weren't getting called on SIGINT on Windows.\n- Fix a data race between app.Done() and shutdown.\n\n## [1.15.0](https://github.com/uber-go/fx/compare/v1.14.2...v1.15.0) - 2021-11-08\n\n### Added\n- Add `fx.Annotate` to allow users to provide parameter and result tags easily without\n  having to create `fx.In` or `fx.Out` structs.\n- Add `fx.As` that allows users to annotate a constructor to provide its result type(s) as\n  interface(s) that they implement instead of the types themselves.\n\n### Fixed\n- Fix `fxevent.Stopped` not being logged when `App.Stop` is called.\n- Fix `fxevent.Started` or `fxevent.Stopped` not being logged when start or\n  stop times out.\n\n## [1.14.2](https://github.com/uber-go/fx/compare/v1.14.1...v1.14.2) - 2021-08-16\n\n### Changed\n- For `fxevent` console implementation: no longer log non-error case for `fxevent.Invoke`\n  event, while for zap implementation, start logging `fx.Invoking` case without stack.\n\n## [1.14.1](https://github.com/uber-go/fx/compare/v1.14.0...v1.14.1) - 2021-08-16\n\n### Changed\n- `fxevent.Invoked` was being logged at `Error` level even upon successful `Invoke`.\n  This was changed to log at `Info` level when `Invoke` succeeded.\n\n## [1.14.0](https://github.com/uber-go/fx/compare/v1.13.1...v1.14.0) - 2021-08-12\n\n### Added\n- Introduce the new `fx.WithLogger` option. Provide a constructor for\n  `fxevent.Logger` objects with it to customize how Fx logs events.\n- Add new `fxevent` package that exposes events from Fx in a structured way.\n  Use this to write custom logger implementations for use with the\n  `fx.WithLogger` option.\n- Expose and log additional information when lifecycle hooks time out.\n\n### Changed\n- Fx now emits structured JSON logs by default. These may be parsed and\n  processed by log ingestion systems.\n- `fxtest.Lifecycle` now logs to the provided `testing.TB` instead of stderr.\n- `fx.In` and `fx.Out` are now type aliases instead of structs.\n\n## [1.13.1](https://github.com/uber-go/fx/compare/v1.13.0...v1.13.1) - 2020-08-19\n\n### Fixed\n- Fix minimum version constraint for dig. `fx.ValidateGraph` requires at least\n  dig 1.10.\n\n## [1.13.0](https://github.com/uber-go/fx/compare/v1.12.0...v1.13.0) - 2020-06-16\n\n### Added\n- Added `fx.ValidateGraph` which allows graph cycle validation and dependency correctness\n  without running anything. This is useful if `fx.Invoke` has side effects, does I/O, etc.\n\n## [1.12.0](https://github.com/uber-go/fx/compare/v1.11.0...v1.12.0) - 2020-04-09\n\n### Added\n- Added `fx.Supply` to provide externally created values to Fx containers\n  without building anonymous constructors.\n\n### Changed\n- Drop library dependency on development tools.\n\n## [1.11.0](https://github.com/uber-go/fx/compare/v1.10.0...v1.11.0) - 2020-04-01\n\n### Added\n- Value groups can use the `flatten` option to indicate values in a slice should\n  be provided individually rather than providing the slice itself. See package\n  documentation for details.\n\n## [1.10.0](https://github.com/uber-go/fx/compare/v1.9.0...v1.10.0) - 2019-11-20\n\n### Added\n- All `fx.Option`s now include readable string representations.\n- Report stack traces when `fx.Provide` and `fx.Invoke` calls fail. This\n  should make these errors more debuggable.\n\n### Changed\n- Migrated to Go modules.\n\n## [1.9.0](https://github.com/uber-go/fx/compare/v1.8.0...v1.9.0) - 2019-01-22\n\n### Added\n- Add the ability to shutdown Fx applications from inside the container. See\n  the Shutdowner documentation for details.\n- Add `fx.Annotated` to allow users to provide named values without creating a\n  new constructor.\n\n## [1.8.0](https://github.com/uber-go/fx/compare/v1.7.1...v1.8.0) - 2018-11-06\n\n### Added\n- Provide DOT graph of dependencies in the container.\n\n## [1.7.1](https://github.com/uber-go/fx/compare/v1.7.0...v1.7.1) - 2018-09-26\n\n### Fixed\n- Make `fxtest.New` ensure that the app was created successfully. Previously,\n  it would return the app (similar to `fx.New`, which expects the user to verify\n  the error).\n- Update dig container to defer acyclic validation until after Invoke. Application\n  startup time should improve proportional to the size of the dependency graph.\n- Fix a goroutine leak in `fxtest.Lifecycle`.\n\n## [1.7.0](https://github.com/uber-go/fx/compare/v1.6.0...v1.7.0) - 2018-08-16\n\n### Added\n- Add `fx.ErrorHook` option to allow users to provide `ErrorHandler`s on invoke\n  failures.\n- `VisualizeError` returns the visualization wrapped in the error if available.\n\n## [1.6.0](https://github.com/uber-go/fx/compare/v1.5.0...v1.6.0) - 2018-06-12\n\n### Added\n- Add `fx.Error` option to short-circuit application startup.\n\n## [1.5.0](https://github.com/uber-go/fx/compare/v1.4.0...v1.5.0) - 2018-04-11\n\n### Added\n- Add `fx.StartTimeout` and `fx.StopTimeout` to make configuring application\n  start and stop timeouts easier.\n- Export the default start and stop timeout as `fx.DefaultTimeout`.\n\n### Fixed\n- Make `fxtest` respect the application's start and stop timeouts.\n\n## [1.4.0](https://github.com/uber-go/fx/compare/v1.3.0...v1.4.0) - 2017-12-07\n\n### Added\n- Add `fx.Populate` to populate variables with values from the dependency\n  injection container without requiring intermediate structs.\n\n## [1.3.0](https://github.com/uber-go/fx/compare/v1.2.0...v1.3.0) - 2017-11-28\n\n### Changed\n- Improve readability of hook logging in addition to provide and invoke.\n\n### Fixed\n- Fix bug which caused the OnStop for a lifecycle hook to be called even if it\n  failed to start.\n\n## [1.2.0](https://github.com/uber-go/fx/compare/v1.1.0...v1.2.0) - 2017-09-06\n\n### Added\n- Add `fx.NopLogger` which disables the Fx application's log output.\n\n## [1.1.0](https://github.com/uber-go/fx/compare/v1.0.0...v1.1.0) - 2017-08-22\n\n### Changed\n- Improve readability of start up logging.\n\n## [1.0.0](https://github.com/uber-go/fx/compare/v1.0.0-rc2...v1.0.0) - 2017-07-31\n\nFirst stable release: no breaking changes will be made in the 1.x series.\n\n### Added\n- `fx.Extract` now supports `fx.In` tags on target structs.\n\n### Changed\n- **[Breaking]** Rename `fx.Inject` to `fx.Extract`.\n- **[Breaking]** Rename `fxtest.Must*` to `fxtest.Require*`.\n\n### Removed\n- **[Breaking]** Remove `fx.Timeout` and `fx.DefaultTimeout`.\n\n## [1.0.0-rc2](https://github.com/uber-go/fx/compare/v1.0.0-rc1...v1.0.0-rc2) - 2017-07-21\n\n- **[Breaking]** Lifecycle hooks now take a context.\n- Add `fx.In` and `fx.Out` which exposes optional and named types.\n  Modules should embed these types instead of relying on `dig.In` and `dig.Out`.\n- Add an `Err` method to retrieve the underlying errors during the dependency\n  graph construction. The same error is also returned from `Start`.\n- Graph resolution now happens as part of `fx.New`, rather than at the beginning\n  of `app.Start`. This allows inspection of the graph errors through `app.Err()`\n  before the decision to start the app.\n- Add a `Logger` option, which allows users to send Fx's logs to different\n  sink.\n- Add `fxtest.App`, which redirects log output to the user's `testing.TB` and\n  provides some lifecycle helpers.\n\n## [1.0.0-rc1](https://github.com/uber-go/fx/compare/v1.0.0-beta4...v1.0.0-rc1) - 2017-06-20\n\n- **[Breaking]** Providing types into `fx.App` and invoking functions are now\n  options passed during application construction. This makes users'\n  interactions with modules and collections of modules identical.\n- **[Breaking]** `TestLifecycle` is now in a separate `fxtest` subpackage.\n- Add `fx.Inject()` to pull values from the container into a struct.\n\n## [1.0.0-beta4](https://github.com/uber-go/fx/compare/v1.0.0-beta3...v1.0.0-beta4) - 2017-06-12\n\n- **[Breaking]** Monolithic framework, as released in initial betas, has been\n  broken into smaller pieces as a result of recent advances in `dig` library.\n  This is a radical departure from the previous direction, but it needed to\n  be done for the long-term good of the project.\n- **[Breaking]** `Module interface` has been scoped all the way down to being\n  *a single dig constructor*. This allows for very sophisticated module\n  compositions. See `go.uber.org/dig` for more information on the constructors.\n- **[Breaking]** `package config` has been moved to its own repository.\n  see `go.uber.org/config` for more information.\n- `fx.Lifecycle` has been added for modules to hook into the framework\n  lifecycle events.\n- `service.Host` interface which composed a number of primitives together\n  (configuration, metrics, tracing) has been deprecated in favor of\n  `fx.App`.\n\n## [1.0.0-beta3](https://github.com/uber-go/fx/compare/v1.0.0-beta2...v1.0.0-beta3) - 2017-03-28\n\n- **[Breaking]** Environment config provider was removed. If you were using\n  environment variables to override YAML values, see config documentation for\n  more information.\n- **[Breaking]** Simplify Provider interface: remove `Scope` method from the\n  `config.Provider` interface, one can use either ScopedProvider and Value.Get()\n  to access sub fields.\n- Add `task.MustRegister` convenience function which fails fast by panicking\n  Note that this should only be used during app initialization, and is provided\n  to avoid repetetive error checking for services which register many tasks.\n- Expose options on task module to disable execution. This will allow users to\n  enqueue and consume tasks on different clusters.\n- **[Breaking]** Rename Backend interface `Publish` to `Enqueue`. Created a new\n  `ExecuteAsync` method that will kick off workers to consume tasks and this is\n  subsumed by module Start.\n- **[Breaking]** Rename package `uhttp/client` to `uhttp/uhttpclient` for clarity.\n- **[Breaking]** Rename `PopulateStruct` method in value to `Populate`.\n  The method can now populate not only structs, but anything: slices,\n  maps, builtin types and maps.\n- **[Breaking]** `package dig` has moved from `go.uber.org/fx/dig` to a new home\n  at `go.uber.org/dig`.\n- **[Breaking]** Pass a tracer the `uhttp/uhttpclient` constructor explicitly, instead\n  of using a global tracer. This will allow to use http client in parallel tests.\n\n## [1.0.0-beta2](https://github.com/uber-go/fx/compare/v1.0.0-beta1...v1.0.0-beta2) - 2017-03-09\n\n- **[Breaking]** Remove `ulog.Logger` interface and expose `*zap.Logger` directly.\n- **[Breaking]** Rename config and module from `modules.rpc` to `modules.yarpc`\n- **[Breaking]** Rename config key from `modules.http` to `modules.uhttp` to match\n  the module name\n- **[Breaking]** Upgrade `zap` to `v1.0.0-rc.3` (now go.uber.org/zap, was\n  github.com/uber-go/zap)\n- Remove now-unused `config.IsDevelopmentEnv()` helper to encourage better\n  testing practices. Not a breaking change as nobody is using this func\n  themselves according to our code search tool.\n- Log `traceID` and `spanID` in hex format to match Jaeger UI. Upgrade Jaeger to\n  min version 2.1.0\n  and use jaeger's adapters for jaeger and tally initialization.\n- Tally now supports reporting histogram samples for a bucket. Upgrade Tally to 2.1.0\n- **[Breaking]** Make new module naming consistent `yarpc.ThriftModule` to\n  `yarpc.New`, `task.NewModule`\n  to `task.New`\n- **[Breaking]** Rename `yarpc.CreateThriftServiceFunc` to `yarpc.ServiceCreateFunc`\n  as it is not thrift-specific.\n- Report version metrics for company-wide version usage information.\n- Allow configurable service name and module name via service options.\n- DIG constructors now support returning a tuple with the second argument being\n  an error.\n\n## 1.0.0-beta1 - 2017-02-20\n\nThis is the first beta release of the framework, where we invite users to start\nbuilding services on it and provide us feedback. **Warning** we are not\npromising API compatibility between beta releases and the final 1.0.0 release.\nIn fact, we expect our beta user feedback to require some changes to the way\nthings work. Once we reach 1.0, we will provider proper version compatibility.\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "---\nsearch:\n  exclude: true\n---\n\n# Contributing\n\nThanks for helping to make Fx better for everyone!\n\nIf you'd like to add new exported APIs,\nplease [open an issue](https://github.com/uber-go/fx/issues/new)\ndescribing your proposal.\nDiscussing API changes ahead of time makes pull request review much smoother.\n\n!!! tip\n\n    You'll need to sign [Uber's CLA](https://cla-assistant.io/uber-go/fx)\n    before we can accept any of your contributions.\n    If necessary, a bot will remind\n    you to accept the CLA when you open your pull request.\n\n\n## Contribute code\n\nSet up your local development environment to contribute to Fx.\n\n1. [Fork](https://github.com/uber-go/fx/fork), then clone the repository.\n\n    === \"Git\"\n\n        ```bash\n        git clone https://github.com/your_github_username/fx.git\n        cd fx\n        git remote add upstream https://github.com/uber-go/fx.git\n        git fetch upstream\n        ```\n\n    === \"GitHub CLI\"\n\n        ```bash\n        gh repo fork --clone uber-go/fx\n        ```\n\n2. Install Fx's dependencies:\n\n     ```bash\n     go mod download\n     ```\n\n3. Verify that tests and other checks pass locally.\n\n     ```bash\n     make lint\n     make test\n     ```\n\n     Note that for `make lint` to work,\n     you must be using the latest stable version of Go.\n     If you're on an older version, you can still contribute your change,\n     but we may discover style violations when you open the pull request.\n\nNext, make your changes.\n\n1. Create a new feature branch.\n\n     ```bash\n     git checkout master\n     git pull\n     git checkout -b cool_new_feature\n     ```\n\n2. Make your changes, and verify that all tests and lints still pass.\n\n     ```bash\n     $EDITOR app.go\n     make lint\n     make test\n     ```\n\n3. When you're satisfied with the change,\n   push it to your fork and make a pull request.\n\n    === \"Git\"\n\n        ```bash\n        git push origin cool_new_feature\n        # Open a PR at https://github.com/uber-go/fx/compare\n        ```\n\n    === \"GitHub CLI\"\n\n        ```bash\n        gh pr create\n        ```\n\nAt this point, you're waiting on us to review your changes.\nWe *try* to respond to issues and pull requests within a few business days,\nand we may suggest some improvements or alternatives.\nOnce your changes are approved, one of the project maintainers will merge them.\n\nThe review process will go more smoothly if you:\n\n- add tests for new functionality\n- write a [good commit message](https://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html)\n- maintain backward compatibility\n- follow our [style guide](https://github.com/uber-go/guide/blob/master/style.md)\n\n## Contribute documentation\n\nTo contribute documentation to Fx,\n\n1. Set up your local development environment\n   as you would to [contribute code](#contribute-code).\n\n2. [Install uv](https://docs.astral.sh/uv/getting-started/installation/).\n   We use this to manage our Python dependencies.\n\n3. Run the development server.\n\n     ```bash\n     make serve\n     ```\n\n4. Make your changes.\n\nDocumentation changes should adhere to the guidance laid out below.\n\n### Document by purpose\n\nDocumentation is organized in one of the following categories.\n\n- **Tutorials**: These hold step-by-step instructions for an end-to-end project\n  that a beginner could follow along to.\n  Don't spend time explaining things.\n  If explanations are available elsewhere, link to them.\n  These are entry points to answer the prompt,\n  \"I don't know what Fx is, show me what it can do,\"\n  so there won't be too many of these.\n- **Explanations**: These hold long-form explanations of concepts and ideas.\n  These are intended to build an understanding of Fx.\n  Feel free to go wild here--use learning aids like diagrams, tables, etc.\n- **How-tos**: These are step-by-step instructions for a *specific problem*.\n  Unlike tutorials, these are not meant to be end-to-end.\n  Feel free to leave things out, make assumptions,\n  or provide options (\"if you're doing this, do this\").\n  As with tutorials, don't spend time explaining;\n  link to explanations elsewhere.\n\nAs an example,\n\n- A tutorial will use lifecycle hooks as part of\n  a larger set of instructions for a full end-to-end application.\n- An explanation will explain what lifecycle hooks are, how they work,\n  when and how you should use them, and link to relevant APIs and guides.\n- A how-to guide will demonstrate how to use lifecycle hooks\n  with an HTTP server, a gRPC server, etc.\n\nExplanations and how-to guides are often on the same page,\nbut they should be in distinct sections.\n\nThis separation is inspired by the\n[Divio documentation system](https://documentation.divio.com/),\n\n### Formatting\n\n#### ATX-style headers\n\nUse ATX-style headers (`#`-prefixed),\nnot Setext-style (underlined with `===` or `---`).\n\n```markdown\nBad header\n==========\n\n## Good header\n```\n\n#### Semantic Line Breaks\n\n- **Do not** write overly long lines of text\n- **Do not** \"reflow\" Markdown paragraphs\n- **Do** use [Semantic Line Breaks](https://sembr.org/) to break these lines down\n\n```markdown\nThis is a bad paragraph because it's really long, all on one line. When I open this in a text editor, I'll have to scroll right.\n\nThis is a bad paragraph because even though it's not all one one line, it adds\nline breaks when it reaches the line length limit. This means that anytime I\nchange anything in this paragraph, I have to \"reflow\" it, which will change\nother lines and make the change I'm making more difficult to review.\n\nThis is a good paragraph. It uses semantic line breaks.\nI can add words or modify an existing sentence,\nor even parts of a sentence,\neasily and without affecting other lines.\nWhen I change something, the actual change I made is easy to review.\nMarkdown will reflow this into a \"normal\" pargraph when rendering.\n```\n\n### Test everything\n\nAll code samples in documentation must be buildable and testable.\n\nTo make this possible, we put code samples in the \"ex/\" directory,\nand use the [PyMdown Snippets extension](https://facelessuser.github.io/pymdown-extensions/extensions/snippets/)\nto include them in the documentation.\n\nTo include code snippets in your documentation,\ntake the following steps:\n\n1. Add source code under the `ex/` directory.\n   Usually, the file will be placed in a directory\n   with a name matching the documentation file\n   that will include the snippet.\n\n     For example,\n     for src/annotation.md, examples will reside in ex/annotation/.\n\n2. Inside the source file, name regions of code with comments in the forms:\n\n     ```\n     // --8<-- [start:name]\n     ...\n     // --8<-- [end:name]\n     ```\n\n     Where `name` is the name of the snippet.\n     For example:\n\n     ```go\n     // --8<-- [start:New]\n     func New() *http.Server {\n         // ...\n     }\n     // --8<-- [end:New]\n     ```\n\n3. Include the snippet in a code block with the following syntax:\n\n    ```markdown\n    ```go\n    ;--8<-- \"path/to/file.go:name\"\n    ```\n\n    Where `path/to/file.go` is the path to the file containing the snippet\n    relative to the `ex/` directory,\n    and `name` is the name of the snippet.\n\n    You can include multiple snippets from the same file like so:\n\n    ```\n    ;--8<-- \"path/to/file.go:snippet1\"\n    ;--8<-- \"path/to/file.go:snippet2\"\n    ```\n"
  },
  {
    "path": "LICENSE",
    "content": "Copyright (c) 2016-2018 Uber Technologies, Inc.\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\nall copies 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\nTHE SOFTWARE.\n"
  },
  {
    "path": "Makefile",
    "content": "# Directory containing the Makefile.\nPROJECT_ROOT = $(dir $(abspath $(lastword $(MAKEFILE_LIST))))\n\nexport GOBIN ?= $(PROJECT_ROOT)/bin\nexport PATH := $(GOBIN):$(PATH)\n\nFXLINT = $(GOBIN)/fxlint\n\nMODULES = . ./tools ./docs ./internal/e2e\n\n# 'make cover' should not run on docs by default.\n# We run that separately explicitly on a specific platform.\nCOVER_MODULES ?= $(filter-out ./docs,$(MODULES))\n\n.PHONY: all\nall: build lint test\n\n.PHONY: build\nbuild:\n\tgo build ./...\n\n.PHONY: lint\nlint: golangci-lint tidy-lint fx-lint\n\n.PHONY: test\ntest:\n\t@$(foreach dir,$(MODULES),(cd $(dir) && go test -race ./...) &&) true\n\n.PHONY: cover\ncover:\n\t@$(foreach dir,$(COVER_MODULES), \\\n\t\t(cd $(dir) && \\\n\t\techo \"[cover] $(dir)\" && \\\n\t\tgo test -race -coverprofile=cover.out -coverpkg=./... ./... && \\\n\t\tgo tool cover -html=cover.out -o cover.html) &&) true\n\n.PHONY: tidy\ntidy:\n\t@$(foreach dir,$(MODULES),(cd $(dir) && go mod tidy) &&) true\n\n.PHONY: docs\ndocs:\n\tcd docs && make build\n\n.PHONY: golangci-lint\ngolangci-lint:\n\t@$(foreach mod,$(MODULES), \\\n\t\t(cd $(mod) && \\\n\t\techo \"[lint] golangci-lint: $(mod)\" && \\\n\t\tgolangci-lint run --path-prefix $(mod)) &&) true\n\n.PHONY: tidy-lint\ntidy-lint:\n\t@$(foreach mod,$(MODULES), \\\n\t\t(cd $(mod) && \\\n\t\techo \"[lint] tidy: $(mod)\" && \\\n\t\tgo mod tidy -diff) &&) true\n\n.PHONY: fx-lint\nfx-lint: $(FXLINT)\n\t@$(FXLINT) ./...\n\n$(FXLINT): tools/cmd/fxlint/main.go\n\tcd tools && go install go.uber.org/fx/tools/cmd/fxlint\n"
  },
  {
    "path": "README.md",
    "content": "# :unicorn: Fx [![GoDoc](https://pkg.go.dev/badge/go.uber.org/fx)](https://pkg.go.dev/go.uber.org/fx) [![Github release](https://img.shields.io/github/release/uber-go/fx.svg)](https://github.com/uber-go/fx/releases) [![Build Status](https://github.com/uber-go/fx/actions/workflows/go.yml/badge.svg)](https://github.com/uber-go/fx/actions/workflows/go.yml) [![Coverage Status](https://codecov.io/gh/uber-go/fx/branch/master/graph/badge.svg)](https://codecov.io/gh/uber-go/fx/branch/master) [![Go Report Card](https://goreportcard.com/badge/go.uber.org/fx)](https://goreportcard.com/report/go.uber.org/fx)\n\nFx is a dependency injection system for Go.\n\n**Benefits**\n\n- Eliminate globals: Fx helps you remove global state from your application.\n  No more `init()` or global variables. Use Fx-managed singletons.\n- Code reuse: Fx lets teams within your organization build loosely-coupled\n  and well-integrated shareable components.\n- Battle tested: Fx is the backbone of nearly all Go services at Uber.\n\nSee our [docs](https://uber-go.github.io/fx/) to get started and/or\nlearn more about Fx.\n\n## Installation\n\nUse Go modules to install Fx in your application.\n\n```shell\ngo get go.uber.org/fx@v1\n```\n\n## Getting started\n\nTo get started with Fx, [start here](https://uber-go.github.io/fx/get-started/).\n\n## Stability\n\nThis library is `v1` and follows [SemVer](https://semver.org/) strictly.\n\nNo breaking changes will be made to exported APIs before `v2.0.0`.\n\nThis project follows the [Go Release Policy](https://golang.org/doc/devel/release.html#policy). Each major\nversion of Go is supported until there are two newer major releases.\n\n## Stargazers over time\n\n[![Stargazers over time](https://starchart.cc/uber-go/fx.svg)](https://starchart.cc/uber-go/fx)\n\n"
  },
  {
    "path": "annotated.go",
    "content": "// Copyright (c) 2020-2021 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage fx\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"reflect\"\n\t\"strings\"\n\n\t\"go.uber.org/dig\"\n\t\"go.uber.org/fx/internal/fxreflect\"\n)\n\n// Annotated annotates a constructor provided to Fx with additional options.\n//\n// For example,\n//\n//\tfunc NewReadOnlyConnection(...) (*Connection, error)\n//\n//\tfx.Provide(fx.Annotated{\n//\t  Name: \"ro\",\n//\t  Target: NewReadOnlyConnection,\n//\t})\n//\n// Is equivalent to,\n//\n//\ttype result struct {\n//\t  fx.Out\n//\n//\t  Connection *Connection `name:\"ro\"`\n//\t}\n//\n//\tfx.Provide(func(...) (result, error) {\n//\t  conn, err := NewReadOnlyConnection(...)\n//\t  return result{Connection: conn}, err\n//\t})\n//\n// Annotated cannot be used with constructors which produce fx.Out objects.\n// When used with [Supply], Target is a value instead of a constructor.\n//\n// This type represents a less powerful version of the [Annotate] construct;\n// prefer [Annotate] where possible.\ntype Annotated struct {\n\t// If specified, this will be used as the name for all non-error values returned\n\t// by the constructor. For more information on named values, see the documentation\n\t// for the fx.Out type.\n\t//\n\t// A name option may not be provided if a group option is provided.\n\tName string\n\n\t// If specified, this will be used as the group name for all non-error values returned\n\t// by the constructor. For more information on value groups, see the package documentation.\n\t//\n\t// A group option may not be provided if a name option is provided.\n\t//\n\t// Similar to group tags, the group name may be followed by a `,flatten`\n\t// option to indicate that each element in the slice returned by the\n\t// constructor should be injected into the value group individually.\n\tGroup string\n\n\t// Target is the constructor or value being annotated with fx.Annotated.\n\tTarget any\n}\n\nfunc (a Annotated) String() string {\n\tvar fields []string\n\tif len(a.Name) > 0 {\n\t\tfields = append(fields, fmt.Sprintf(\"Name: %q\", a.Name))\n\t}\n\tif len(a.Group) > 0 {\n\t\tfields = append(fields, fmt.Sprintf(\"Group: %q\", a.Group))\n\t}\n\tif a.Target != nil {\n\t\tfields = append(fields, fmt.Sprintf(\"Target: %v\", fxreflect.FuncName(a.Target)))\n\t}\n\treturn fmt.Sprintf(\"fx.Annotated{%v}\", strings.Join(fields, \", \"))\n}\n\nvar (\n\t// field used for embedding fx.In type in generated struct.\n\t_inAnnotationField = reflect.StructField{\n\t\tName:      \"In\",\n\t\tType:      reflect.TypeOf(In{}),\n\t\tAnonymous: true,\n\t}\n\t// field used for embedding fx.Out type in generated struct.\n\t_outAnnotationField = reflect.StructField{\n\t\tName:      \"Out\",\n\t\tType:      reflect.TypeOf(Out{}),\n\t\tAnonymous: true,\n\t}\n)\n\n// Annotation specifies how to wrap a target for [Annotate].\n// It can be used to set up additional options for a constructor,\n// or with [Supply], for a value.\ntype Annotation interface {\n\tapply(*annotated) error\n\tbuild(*annotated) (any, error)\n}\n\nvar (\n\t_typeOfError = reflect.TypeOf((*error)(nil)).Elem()\n\t_nilError    = reflect.Zero(_typeOfError)\n)\n\n// annotationError is a wrapper for an error that was encountered while\n// applying annotation to a function. It contains the specific error\n// that it encountered as well as the target interface that was attempted\n// to be annotated.\ntype annotationError struct {\n\ttarget any\n\terr    error\n}\n\nfunc (e *annotationError) Error() string {\n\treturn e.err.Error()\n}\n\n// Unwrap the wrapped error.\nfunc (e *annotationError) Unwrap() error {\n\treturn e.err\n}\n\ntype paramTagsAnnotation struct {\n\ttags []string\n}\n\nvar _ Annotation = paramTagsAnnotation{}\nvar (\n\terrTagSyntaxSpace            = errors.New(`multiple tags are not separated by space`)\n\terrTagKeySyntax              = errors.New(\"tag key is invalid, Use group, name or optional as tag keys\")\n\terrTagValueSyntaxQuote       = errors.New(`tag value should start with double quote. i.e. key:\"value\" `)\n\terrTagValueSyntaxEndingQuote = errors.New(`tag value should end in double quote. i.e. key:\"value\" `)\n)\n\n// Collections of key value pairs within a tag should be separated by a space.\n// Eg: `group:\"some\" optional:\"true\"`.\nfunc verifyTagsSpaceSeparated(tagIdx int, tag string) error {\n\tif tagIdx > 0 && tag != \"\" && tag[0] != ' ' {\n\t\treturn errTagSyntaxSpace\n\t}\n\treturn nil\n}\n\n// verify tag values are delimited with double quotes.\nfunc verifyValueQuote(value string) (string, error) {\n\t// starting quote should be a double quote\n\tif value[0] != '\"' {\n\t\treturn \"\", errTagValueSyntaxQuote\n\t}\n\t// validate tag value is within quotes\n\ti := 1\n\tfor i < len(value) && value[i] != '\"' {\n\t\tif value[i] == '\\\\' {\n\t\t\ti++\n\t\t}\n\t\ti++\n\t}\n\tif i >= len(value) {\n\t\treturn \"\", errTagValueSyntaxEndingQuote\n\t}\n\treturn value[i+1:], nil\n}\n\n// Check whether the tag follows valid struct.\n// format and returns an error if it's invalid. (i.e. not following\n// tag:\"value\" space-separated list )\n// Currently dig accepts only 'name', 'group', 'optional' as valid tag keys.\nfunc verifyAnnotateTag(tag string) error {\n\ttagIdx := 0\n\tvalidKeys := map[string]struct{}{\"group\": {}, \"optional\": {}, \"name\": {}}\n\tfor ; tag != \"\"; tagIdx++ {\n\t\tif err := verifyTagsSpaceSeparated(tagIdx, tag); err != nil {\n\t\t\treturn err\n\t\t}\n\t\ti := 0\n\t\tif strings.TrimSpace(tag) == \"\" {\n\t\t\treturn nil\n\t\t}\n\t\t// parsing the key i.e. till reaching colon :\n\t\tfor i < len(tag) && tag[i] != ':' {\n\t\t\ti++\n\t\t}\n\t\tkey := strings.TrimSpace(tag[:i])\n\t\tif _, ok := validKeys[key]; !ok {\n\t\t\treturn errTagKeySyntax\n\t\t}\n\t\tvalue, err := verifyValueQuote(tag[i+1:])\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\ttag = value\n\t}\n\treturn nil\n}\n\n// Given func(T1, T2, T3, ..., TN), this generates a type roughly\n// equivalent to,\n//\n//   struct {\n//     fx.In\n//\n//     Field1 T1 `$tags[0]`\n//     Field2 T2 `$tags[1]`\n//     ...\n//     FieldN TN `$tags[N-1]`\n//   }\n//\n// If there has already been a ParamTag that was applied, this\n// will return an error.\n//\n// If the tag is invalid and has mismatched quotation for example,\n// (`tag_name:\"tag_value') , this will return an error.\n\nfunc (pt paramTagsAnnotation) apply(ann *annotated) error {\n\tif len(ann.ParamTags) > 0 {\n\t\treturn errors.New(\"cannot apply more than one line of ParamTags\")\n\t}\n\tfor _, tag := range pt.tags {\n\t\tif err := verifyAnnotateTag(tag); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tann.ParamTags = pt.tags\n\treturn nil\n}\n\n// build builds and returns a constructor after applying a ParamTags annotation\nfunc (pt paramTagsAnnotation) build(ann *annotated) (any, error) {\n\tparamTypes, remap := pt.parameters(ann)\n\tresultTypes, _ := ann.currentResultTypes()\n\n\torigFn := reflect.ValueOf(ann.Target)\n\tnewFnType := reflect.FuncOf(paramTypes, resultTypes, false)\n\tnewFn := reflect.MakeFunc(newFnType, func(args []reflect.Value) []reflect.Value {\n\t\targs = remap(args)\n\t\treturn origFn.Call(args)\n\t})\n\treturn newFn.Interface(), nil\n}\n\n// parameters returns the type for the parameters of the annotated function,\n// and a function that maps the arguments of the annotated function\n// back to the arguments of the target function.\nfunc (pt paramTagsAnnotation) parameters(ann *annotated) (\n\ttypes []reflect.Type,\n\tremap func([]reflect.Value) []reflect.Value,\n) {\n\tft := reflect.TypeOf(ann.Target)\n\ttypes = make([]reflect.Type, ft.NumIn())\n\tfor i := 0; i < ft.NumIn(); i++ {\n\t\ttypes[i] = ft.In(i)\n\t}\n\n\t// No parameter annotations. Return the original types\n\t// and an identity function.\n\tif len(pt.tags) == 0 {\n\t\treturn types, func(args []reflect.Value) []reflect.Value {\n\t\t\treturn args\n\t\t}\n\t}\n\n\t// Turn parameters into an fx.In struct.\n\tinFields := []reflect.StructField{_inAnnotationField}\n\n\t// there was a variadic argument, so it was pre-transformed\n\tif len(types) > 0 && isIn(types[0]) {\n\t\tparamType := types[0]\n\n\t\tfor i := 1; i < paramType.NumField(); i++ {\n\t\t\torigField := paramType.Field(i)\n\t\t\tfield := reflect.StructField{\n\t\t\t\tName: origField.Name,\n\t\t\t\tType: origField.Type,\n\t\t\t\tTag:  origField.Tag,\n\t\t\t}\n\t\t\tif i-1 < len(pt.tags) {\n\t\t\t\tfield.Tag = reflect.StructTag(pt.tags[i-1])\n\t\t\t}\n\n\t\t\tinFields = append(inFields, field)\n\t\t}\n\n\t\ttypes = []reflect.Type{reflect.StructOf(inFields)}\n\t\treturn types, func(args []reflect.Value) []reflect.Value {\n\t\t\tparam := args[0]\n\t\t\targs[0] = reflect.New(paramType).Elem()\n\t\t\tfor i := 1; i < paramType.NumField(); i++ {\n\t\t\t\targs[0].Field(i).Set(param.Field(i))\n\t\t\t}\n\t\t\treturn args\n\t\t}\n\t}\n\n\tfor i, t := range types {\n\t\tfield := reflect.StructField{\n\t\t\tName: fmt.Sprintf(\"Field%d\", i),\n\t\t\tType: t,\n\t\t}\n\t\tif i < len(pt.tags) {\n\t\t\tfield.Tag = reflect.StructTag(pt.tags[i])\n\t\t}\n\n\t\tinFields = append(inFields, field)\n\t}\n\n\ttypes = []reflect.Type{reflect.StructOf(inFields)}\n\treturn types, func(args []reflect.Value) []reflect.Value {\n\t\tparams := args[0]\n\t\targs = args[:0]\n\t\tfor i := 0; i < ft.NumIn(); i++ {\n\t\t\targs = append(args, params.Field(i+1))\n\t\t}\n\t\treturn args\n\t}\n}\n\n// ParamTags is an Annotation that annotates the parameter(s) of a function.\n//\n// When multiple tags are specified, each tag is mapped to the corresponding\n// positional parameter.\n// For example, the following will refer to a named database connection,\n// and the default, unnamed logger:\n//\n//\tfx.Annotate(func(log *log.Logger, conn *sql.DB) *Handler {\n//\t\t// ...\n//\t}, fx.ParamTags(\"\", `name:\"ro\"`))\n//\n// ParamTags cannot be used in a function that takes an fx.In struct as a\n// parameter.\nfunc ParamTags(tags ...string) Annotation {\n\treturn paramTagsAnnotation{tags}\n}\n\ntype resultTagsAnnotation struct {\n\ttags []string\n}\n\nvar _ Annotation = resultTagsAnnotation{}\n\n// Given func(T1, T2, T3, ..., TN), this generates a type roughly\n// equivalent to,\n//\n//\tstruct {\n//\t  fx.Out\n//\n//\t  Field1 T1 `$tags[0]`\n//\t  Field2 T2 `$tags[1]`\n//\t  ...\n//\t  FieldN TN `$tags[N-1]`\n//\t}\n//\n// If there has already been a ResultTag that was applied, this\n// will return an error.\n//\n// If the tag is invalid and has mismatched quotation for example,\n// (`tag_name:\"tag_value') , this will return an error.\nfunc (rt resultTagsAnnotation) apply(ann *annotated) error {\n\tif len(ann.ResultTags) > 0 {\n\t\treturn errors.New(\"cannot apply more than one line of ResultTags\")\n\t}\n\tfor _, tag := range rt.tags {\n\t\tif err := verifyAnnotateTag(tag); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tann.ResultTags = rt.tags\n\treturn nil\n}\n\n// build builds and returns a constructor after applying a ResultTags annotation\nfunc (rt resultTagsAnnotation) build(ann *annotated) (any, error) {\n\tparamTypes := ann.currentParamTypes()\n\tresultTypes, remapResults := rt.results(ann)\n\torigFn := reflect.ValueOf(ann.Target)\n\tnewFnType := reflect.FuncOf(paramTypes, resultTypes, false)\n\tnewFn := reflect.MakeFunc(newFnType, func(args []reflect.Value) []reflect.Value {\n\t\tresults := origFn.Call(args)\n\t\treturn remapResults(results)\n\t})\n\treturn newFn.Interface(), nil\n}\n\n// results returns the types of the results of the annotated function,\n// and a function that maps the results of the target function,\n// into a result compatible with the annotated function.\nfunc (rt resultTagsAnnotation) results(ann *annotated) (\n\ttypes []reflect.Type,\n\tremap func([]reflect.Value) []reflect.Value,\n) {\n\ttypes, hasError := ann.currentResultTypes()\n\n\tif hasError {\n\t\ttypes = types[:len(types)-1]\n\t}\n\n\t// No result annotations. Return the original types\n\t// and an identity function.\n\tif len(rt.tags) == 0 {\n\t\treturn types, func(results []reflect.Value) []reflect.Value {\n\t\t\treturn results\n\t\t}\n\t}\n\n\t// if there's no Out struct among the return types, there was no As annotation applied\n\t// just replace original result types with an Out struct and apply tags\n\tvar (\n\t\tnewOut       outStructInfo\n\t\texistingOuts []reflect.Type\n\t)\n\n\tnewOut.Fields = []reflect.StructField{_outAnnotationField}\n\tnewOut.Offsets = []int{}\n\n\tfor i, t := range types {\n\t\tif !isOut(t) {\n\t\t\t// this must be from the original function.\n\t\t\t// apply the tags\n\t\t\tfield := reflect.StructField{\n\t\t\t\tName: fmt.Sprintf(\"Field%d\", i),\n\t\t\t\tType: t,\n\t\t\t}\n\t\t\tif i < len(rt.tags) {\n\t\t\t\tfield.Tag = reflect.StructTag(rt.tags[i])\n\t\t\t}\n\t\t\tnewOut.Offsets = append(newOut.Offsets, len(newOut.Fields))\n\t\t\tnewOut.Fields = append(newOut.Fields, field)\n\t\t\tcontinue\n\t\t}\n\t\t// this must be from an As annotation\n\t\t// apply the tags to the existing type\n\t\ttaggedFields := make([]reflect.StructField, t.NumField())\n\t\ttaggedFields[0] = _outAnnotationField\n\t\tfor j, tag := range rt.tags {\n\t\t\tif j+1 < t.NumField() {\n\t\t\t\tfield := t.Field(j + 1)\n\t\t\t\ttaggedFields[j+1] = reflect.StructField{\n\t\t\t\t\tName: field.Name,\n\t\t\t\t\tType: field.Type,\n\t\t\t\t\tTag:  reflect.StructTag(tag),\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\texistingOuts = append(existingOuts, reflect.StructOf(taggedFields))\n\t}\n\n\tresType := reflect.StructOf(newOut.Fields)\n\n\toutTypes := []reflect.Type{resType}\n\t// append existing outs back to outTypes\n\toutTypes = append(outTypes, existingOuts...)\n\tif hasError {\n\t\toutTypes = append(outTypes, _typeOfError)\n\t}\n\n\treturn outTypes, func(results []reflect.Value) []reflect.Value {\n\t\tvar (\n\t\t\toutErr     error\n\t\t\toutResults []reflect.Value\n\t\t)\n\t\toutResults = append(outResults, reflect.New(resType).Elem())\n\n\t\ttIdx := 0\n\t\tfor i, r := range results {\n\t\t\tif i == len(results)-1 && hasError {\n\t\t\t\t// If hasError and this is the last item,\n\t\t\t\t// we are guaranteed that this is an error\n\t\t\t\t// object.\n\t\t\t\tif err, _ := r.Interface().(error); err != nil {\n\t\t\t\t\toutErr = err\n\t\t\t\t}\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif i < len(newOut.Offsets) {\n\t\t\t\tif fieldIdx := newOut.Offsets[i]; fieldIdx > 0 {\n\t\t\t\t\t// fieldIdx 0 is an invalid index\n\t\t\t\t\t// because it refers to uninitialized\n\t\t\t\t\t// outs and would point to fx.Out in the\n\t\t\t\t\t// struct definition. We need to check this\n\t\t\t\t\t// to prevent panic from setting fx.Out to\n\t\t\t\t\t// a value.\n\t\t\t\t\toutResults[0].Field(fieldIdx).Set(r)\n\t\t\t\t}\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif isOut(r.Type()) {\n\t\t\t\ttIdx++\n\t\t\t\tif tIdx < len(outTypes) {\n\t\t\t\t\tnewResult := reflect.New(outTypes[tIdx]).Elem()\n\t\t\t\t\tfor j := 1; j < outTypes[tIdx].NumField(); j++ {\n\t\t\t\t\t\tnewResult.Field(j).Set(r.Field(j))\n\t\t\t\t\t}\n\t\t\t\t\toutResults = append(outResults, newResult)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif hasError {\n\t\t\tif outErr != nil {\n\t\t\t\toutResults = append(outResults, reflect.ValueOf(outErr))\n\t\t\t} else {\n\t\t\t\toutResults = append(outResults, _nilError)\n\t\t\t}\n\t\t}\n\n\t\treturn outResults\n\t}\n}\n\n// ResultTags is an Annotation that annotates the result(s) of a function.\n// When multiple tags are specified, each tag is mapped to the corresponding\n// positional result.\n//\n// For example, the following will produce a named database connection.\n//\n//\tfx.Annotate(func() (*sql.DB, error) {\n//\t\t// ...\n//\t}, fx.ResultTags(`name:\"ro\"`))\n//\n// ResultTags cannot be used on a function that returns an fx.Out struct.\nfunc ResultTags(tags ...string) Annotation {\n\treturn resultTagsAnnotation{tags}\n}\n\ntype outStructInfo struct {\n\tFields  []reflect.StructField // fields of the struct\n\tOffsets []int                 // Offsets[i] is the index of result i in Fields\n}\n\ntype _lifecycleHookAnnotationType int\n\nconst (\n\t_unknownHookType _lifecycleHookAnnotationType = iota\n\t_onStartHookType\n\t_onStopHookType\n)\n\ntype lifecycleHookAnnotation struct {\n\tType   _lifecycleHookAnnotationType\n\tTarget any\n}\n\nvar _ Annotation = (*lifecycleHookAnnotation)(nil)\n\nfunc (la *lifecycleHookAnnotation) String() string {\n\tname := \"UnknownHookAnnotation\"\n\tswitch la.Type {\n\tcase _onStartHookType:\n\t\tname = _onStartHook\n\tcase _onStopHookType:\n\t\tname = _onStopHook\n\t}\n\treturn name\n}\n\nfunc (la *lifecycleHookAnnotation) apply(ann *annotated) error {\n\tif la.Target == nil {\n\t\treturn fmt.Errorf(\n\t\t\t\"cannot use nil function for %q hook annotation\",\n\t\t\tla,\n\t\t)\n\t}\n\n\tfor _, h := range ann.Hooks {\n\t\tif la.Type == h.Type {\n\t\t\treturn fmt.Errorf(\n\t\t\t\t\"cannot apply more than one %q hook annotation\",\n\t\t\t\tla,\n\t\t\t)\n\t\t}\n\t}\n\n\tft := reflect.TypeOf(la.Target)\n\n\tif ft.Kind() != reflect.Func {\n\t\treturn fmt.Errorf(\n\t\t\t\"must provide function for %q hook, got %v (%T)\",\n\t\t\tla,\n\t\t\tla.Target,\n\t\t\tla.Target,\n\t\t)\n\t}\n\n\tif n := ft.NumOut(); n > 0 {\n\t\tif n > 1 || ft.Out(0) != _typeOfError {\n\t\t\treturn fmt.Errorf(\n\t\t\t\t\"optional hook return may only be an error, got %v (%T)\",\n\t\t\t\tla.Target,\n\t\t\t\tla.Target,\n\t\t\t)\n\t\t}\n\t}\n\n\tif ft.IsVariadic() {\n\t\treturn fmt.Errorf(\n\t\t\t\"hooks must not accept variadic parameters, got %v (%T)\",\n\t\t\tla.Target,\n\t\t\tla.Target,\n\t\t)\n\t}\n\n\tann.Hooks = append(ann.Hooks, la)\n\treturn nil\n}\n\n// build builds and returns a constructor after applying a lifecycle hook annotation.\nfunc (la *lifecycleHookAnnotation) build(ann *annotated) (any, error) {\n\tresultTypes, hasError := ann.currentResultTypes()\n\tif !hasError {\n\t\tresultTypes = append(resultTypes, _typeOfError)\n\t}\n\n\thookInstaller, paramTypes, remapParams, err := la.buildHookInstaller(ann)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\torigFn := reflect.ValueOf(ann.Target)\n\tnewFnType := reflect.FuncOf(paramTypes, resultTypes, false)\n\tnewFn := reflect.MakeFunc(newFnType, func(args []reflect.Value) []reflect.Value {\n\t\t// copy the original arguments before remapping the parameters\n\t\t// so that we can apply them to the hookInstaller.\n\t\torigArgs := make([]reflect.Value, len(args))\n\t\tcopy(origArgs, args)\n\t\targs = remapParams(args)\n\t\tresults := origFn.Call(args)\n\t\tif hasError {\n\t\t\terrVal := results[len(results)-1]\n\t\t\tresults = results[:len(results)-1]\n\t\t\tif err, _ := errVal.Interface().(error); err != nil {\n\t\t\t\t// if constructor returned error, do not call hook installer\n\t\t\t\treturn append(results, errVal)\n\t\t\t}\n\t\t}\n\t\thookInstallerResults := hookInstaller.Call(append(results, origArgs...))\n\t\tresults = append(results, hookInstallerResults[0])\n\t\treturn results\n\t})\n\treturn newFn.Interface(), nil\n}\n\nvar (\n\t_typeOfLifecycle = reflect.TypeOf((*Lifecycle)(nil)).Elem()\n\t_typeOfContext   = reflect.TypeOf((*context.Context)(nil)).Elem()\n)\n\n// validateHookDeps validates the dependencies of a hook function and returns true if the dependencies are valid.\nfunc (la *lifecycleHookAnnotation) validateHookDeps(hookParamTypes []reflect.Type, paramTypes []reflect.Type, resultTypes []reflect.Type) (err error) {\n\ttype key struct {\n\t\tt     reflect.Type\n\t\tname  string\n\t\tgroup string\n\t}\n\n\tformatLog := func(k key) error {\n\t\tvar tags []string\n\t\tif len(k.name) > 0 {\n\t\t\ttags = append(tags, fmt.Sprintf(\"name:\\\"%s\\\"\", k.name))\n\t\t}\n\t\tif len(k.group) > 0 {\n\t\t\ttags = append(tags, fmt.Sprintf(\"group:\\\"%s\\\"\", k.group))\n\t\t}\n\t\tvar formatted string\n\t\tif len(tags) > 0 {\n\t\t\tformatted = fmt.Sprintf(\"%s `%s`\", k.t.String(), strings.Join(tags, \" \"))\n\t\t} else {\n\t\t\tformatted = k.t.String()\n\t\t}\n\t\treturn fmt.Errorf(\"the %s hook function takes in a parameter of \\\"%s\\\", but the annotated function does not have parameters or results of that type\", la.String(), formatted)\n\t}\n\terr = nil\n\tseen := make(map[key]struct{})\n\n\tfor _, t := range paramTypes {\n\t\tif !isIn(t) {\n\t\t\tseen[key{t: t}] = struct{}{}\n\t\t\tcontinue\n\t\t}\n\t\tfor i := 1; i < t.NumField(); i++ {\n\t\t\tfield := t.Field(i)\n\t\t\tseen[key{\n\t\t\t\tt:     field.Type,\n\t\t\t\tname:  field.Tag.Get(\"name\"),\n\t\t\t\tgroup: field.Tag.Get(\"group\"),\n\t\t\t}] = struct{}{}\n\t\t}\n\t}\n\tfor _, t := range resultTypes {\n\t\tif !isOut(t) {\n\t\t\tseen[key{t: t}] = struct{}{}\n\t\t\tcontinue\n\t\t}\n\t\tfor i := 1; i < t.NumField(); i++ {\n\t\t\tfield := t.Field(i)\n\t\t\tseen[key{\n\t\t\t\tt:     field.Type,\n\t\t\t\tname:  field.Tag.Get(\"name\"),\n\t\t\t\tgroup: field.Tag.Get(\"group\"),\n\t\t\t}] = struct{}{}\n\t\t}\n\t}\n\tfor _, t := range hookParamTypes {\n\t\tif !isIn(t) {\n\t\t\tk := key{t: t}\n\t\t\tif _, ok := seen[k]; !ok {\n\t\t\t\terr = formatLog(k)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tcontinue\n\t\t}\n\t\tfor i := 1; i < t.NumField(); i++ {\n\t\t\tfield := t.Field(i)\n\t\t\tk := key{\n\t\t\t\tt:     field.Type,\n\t\t\t\tname:  field.Tag.Get(\"name\"),\n\t\t\t\tgroup: field.Tag.Get(\"group\"),\n\t\t\t}\n\t\t\tif _, ok := seen[k]; !ok {\n\t\t\t\terr = formatLog(k)\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t}\n\treturn\n}\n\n// buildHookInstaller returns a function that appends a hook to Lifecycle when called,\n// along with the new parameter types and a function that maps arguments to the annotated constructor\nfunc (la *lifecycleHookAnnotation) buildHookInstaller(ann *annotated) (\n\thookInstaller reflect.Value,\n\tparamTypes []reflect.Type,\n\tremapParams func([]reflect.Value) []reflect.Value, // function to remap parameters to function being annotated\n\terr error,\n) {\n\tparamTypes = ann.currentParamTypes()\n\tparamTypes, remapParams = injectLifecycle(paramTypes)\n\n\tresultTypes, hasError := ann.currentResultTypes()\n\tif hasError {\n\t\tresultTypes = resultTypes[:len(resultTypes)-1]\n\t}\n\n\t// look for the context.Context type from the original hook function\n\t// and then exclude it from the paramTypes of invokeFn because context.Context\n\t// will be injected by the lifecycle\n\tctxPos := -1\n\tctxStructPos := -1\n\torigHookFn := reflect.ValueOf(la.Target)\n\torigHookFnT := reflect.TypeOf(la.Target)\n\tinvokeParamTypes := []reflect.Type{\n\t\t_typeOfLifecycle,\n\t}\n\tfor i := 0; i < origHookFnT.NumIn(); i++ {\n\t\tt := origHookFnT.In(i)\n\t\tif t == _typeOfContext && ctxPos < 0 {\n\t\t\tctxPos = i\n\t\t\tcontinue\n\t\t}\n\n\t\tif !isIn(t) {\n\t\t\tinvokeParamTypes = append(invokeParamTypes, origHookFnT.In(i))\n\t\t\tcontinue\n\t\t}\n\t\tfields := []reflect.StructField{_inAnnotationField}\n\t\tfor j := 1; j < t.NumField(); j++ {\n\t\t\tfield := t.Field(j)\n\t\t\tif field.Type == _typeOfContext && ctxPos < 0 {\n\t\t\t\tctxStructPos = i\n\t\t\t\tctxPos = j\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tfields = append(fields, field)\n\t\t}\n\t\tinvokeParamTypes = append(invokeParamTypes, reflect.StructOf(fields))\n\n\t}\n\tif err = la.validateHookDeps(invokeParamTypes, paramTypes, resultTypes); err != nil {\n\t\treturn\n\t}\n\tinvokeFnT := reflect.FuncOf(invokeParamTypes, []reflect.Type{}, false)\n\tinvokeFn := reflect.MakeFunc(invokeFnT, func(args []reflect.Value) (results []reflect.Value) {\n\t\tlc := args[0].Interface().(Lifecycle)\n\t\targs = args[1:]\n\t\thookArgs := make([]reflect.Value, origHookFnT.NumIn())\n\n\t\thookFn := func(ctx context.Context) (err error) {\n\t\t\t// If the hook function has multiple parameters, and the first\n\t\t\t// parameter is a context, inject the provided context.\n\t\t\tif ctxStructPos < 0 {\n\t\t\t\toffset := 0\n\t\t\t\tfor i := range hookArgs {\n\t\t\t\t\tif i == ctxPos {\n\t\t\t\t\t\thookArgs[i] = reflect.ValueOf(ctx)\n\t\t\t\t\t\toffset = 1\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\t\t\t\t\tif i-offset >= 0 && i-offset < len(args) {\n\t\t\t\t\t\thookArgs[i] = args[i-offset]\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tfor i := 0; i < origHookFnT.NumIn(); i++ {\n\t\t\t\t\tif i != ctxStructPos {\n\t\t\t\t\t\thookArgs[i] = args[i]\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\t\t\t\t\tt := origHookFnT.In(i)\n\t\t\t\t\tv := reflect.New(t).Elem()\n\t\t\t\t\tfor j := 1; j < t.NumField(); j++ {\n\t\t\t\t\t\tif j < ctxPos {\n\t\t\t\t\t\t\tv.Field(j).Set(args[i].Field(j))\n\t\t\t\t\t\t} else if j == ctxPos {\n\t\t\t\t\t\t\tv.Field(j).Set(reflect.ValueOf(ctx))\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tv.Field(j).Set(args[i].Field(j - 1))\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\thookArgs[i] = v\n\t\t\t\t}\n\t\t\t}\n\t\t\thookResults := origHookFn.Call(hookArgs)\n\t\t\tif len(hookResults) > 0 && hookResults[0].Type() == _typeOfError {\n\t\t\t\terr, _ = hookResults[0].Interface().(error)\n\t\t\t}\n\t\t\treturn err\n\t\t}\n\t\tlc.Append(la.buildHook(hookFn))\n\t\treturn results\n\t})\n\n\tinstallerType := reflect.FuncOf(append(resultTypes, paramTypes...), []reflect.Type{_typeOfError}, false)\n\thookInstaller = reflect.MakeFunc(installerType, func(args []reflect.Value) (results []reflect.Value) {\n\t\t// build a private scope for hook function\n\t\tvar scope *dig.Scope\n\t\tswitch la.Type {\n\t\tcase _onStartHookType:\n\t\t\tscope = ann.container.Scope(\"onStartHookScope\")\n\t\tcase _onStopHookType:\n\t\t\tscope = ann.container.Scope(\"onStopHookScope\")\n\t\t}\n\n\t\t// provide the private scope with the current dependencies and results of the annotated function\n\t\tresults = []reflect.Value{_nilError}\n\t\tctor := makeHookScopeCtor(paramTypes, resultTypes, args)\n\t\tif err := scope.Provide(ctor); err != nil {\n\t\t\tresults[0] = reflect.ValueOf(fmt.Errorf(\"error providing possible parameters for hook installer: %w\", err))\n\t\t\treturn results\n\t\t}\n\n\t\t// invoking invokeFn appends the hook function to lifecycle\n\t\tif err := scope.Invoke(invokeFn.Interface()); err != nil {\n\t\t\tresults[0] = reflect.ValueOf(fmt.Errorf(\"error invoking hook installer: %w\", err))\n\t\t\treturn results\n\t\t}\n\t\treturn results\n\t})\n\treturn hookInstaller, paramTypes, remapParams, nil\n}\n\nvar (\n\t_nameTag  = \"name\"\n\t_groupTag = \"group\"\n)\n\n// makeHookScopeCtor makes a constructor that provides all possible parameters\n// that the lifecycle hook being appended can depend on. It also deduplicates\n// duplicate param and result types, which is possible when using fx.Decorate,\n// and uses values from results for providing the deduplicated types.\nfunc makeHookScopeCtor(paramTypes []reflect.Type, resultTypes []reflect.Type, args []reflect.Value) any {\n\ttype key struct {\n\t\tt     reflect.Type\n\t\tname  string\n\t\tgroup string\n\t}\n\tseen := map[key]struct{}{}\n\toutTypes := make([]reflect.Type, len(resultTypes))\n\tfor i, t := range resultTypes {\n\t\toutTypes[i] = t\n\t\tif isOut(t) {\n\t\t\tfor j := 1; j < t.NumField(); j++ {\n\t\t\t\tfield := t.Field(j)\n\t\t\t\tseen[key{\n\t\t\t\t\tt:     field.Type,\n\t\t\t\t\tname:  field.Tag.Get(_nameTag),\n\t\t\t\t\tgroup: field.Tag.Get(_groupTag),\n\t\t\t\t}] = struct{}{}\n\t\t\t}\n\t\t\tcontinue\n\t\t}\n\t\tseen[key{t: t}] = struct{}{}\n\t}\n\n\tfields := []reflect.StructField{_outAnnotationField}\n\n\tskippedParams := make([][]int, len(paramTypes))\n\n\tfor i, t := range paramTypes {\n\t\tskippedParams[i] = []int{}\n\t\tif isIn(t) {\n\t\t\tfor j := 1; j < t.NumField(); j++ {\n\t\t\t\torigField := t.Field(j)\n\t\t\t\tk := key{\n\t\t\t\t\tt:     origField.Type,\n\t\t\t\t\tname:  origField.Tag.Get(_nameTag),\n\t\t\t\t\tgroup: origField.Tag.Get(_groupTag),\n\t\t\t\t}\n\n\t\t\t\tif _, ok := seen[k]; ok {\n\t\t\t\t\tskippedParams[i] = append(skippedParams[i], j)\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\n\t\t\t\tfield := reflect.StructField{\n\t\t\t\t\tName: fmt.Sprintf(\"Field%d\", j-1),\n\t\t\t\t\tType: origField.Type,\n\t\t\t\t\tTag:  origField.Tag,\n\t\t\t\t}\n\t\t\t\tfields = append(fields, field)\n\t\t\t}\n\t\t\tcontinue\n\t\t}\n\t\tk := key{t: t}\n\n\t\tif _, ok := seen[k]; ok {\n\t\t\tskippedParams[i] = append(skippedParams[i], i)\n\t\t\tcontinue\n\t\t}\n\t\tfield := reflect.StructField{\n\t\t\tName: fmt.Sprintf(\"Field%d\", i),\n\t\t\tType: t,\n\t\t}\n\t\tfields = append(fields, field)\n\t}\n\n\toutTypes = append(outTypes, reflect.StructOf(fields))\n\tctorType := reflect.FuncOf([]reflect.Type{}, outTypes, false)\n\tctor := reflect.MakeFunc(ctorType, func(_ []reflect.Value) []reflect.Value {\n\t\tnOut := len(outTypes)\n\t\tresults := make([]reflect.Value, nOut)\n\t\tfor i := 0; i < nOut-1; i++ {\n\t\t\tresults[i] = args[i]\n\t\t}\n\n\t\tv := reflect.New(outTypes[nOut-1]).Elem()\n\t\tfieldIdx := 1\n\t\tfor i := nOut - 1; i < len(args); i++ {\n\t\t\tparamIdx := i - (nOut - 1)\n\t\t\tif isIn(paramTypes[paramIdx]) {\n\t\t\t\tskippedIdx := 0\n\t\t\t\tfor j := 1; j < paramTypes[paramIdx].NumField(); j++ {\n\t\t\t\t\tif len(skippedParams[paramIdx]) > 0 && skippedParams[paramIdx][skippedIdx] == j {\n\t\t\t\t\t\t// skip\n\t\t\t\t\t\tskippedIdx++\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\t\t\t\t\tv.Field(fieldIdx).Set(args[i].Field(j))\n\t\t\t\t\tfieldIdx++\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tif len(skippedParams[paramIdx]) > 0 && skippedParams[paramIdx][0] == paramIdx {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tv.Field(fieldIdx).Set(args[i])\n\t\t\t\tfieldIdx++\n\t\t\t}\n\t\t}\n\t\tresults[nOut-1] = v\n\n\t\treturn results\n\t})\n\treturn ctor.Interface()\n}\n\nfunc injectLifecycle(paramTypes []reflect.Type) ([]reflect.Type, func([]reflect.Value) []reflect.Value) {\n\t// since lifecycle already exists in param types, no need to inject again\n\tif lifecycleExists(paramTypes) {\n\t\treturn paramTypes, func(args []reflect.Value) []reflect.Value {\n\t\t\treturn args\n\t\t}\n\t}\n\t// If params are tagged or there's an untagged variadic argument,\n\t// add a Lifecycle field to the param struct\n\tif len(paramTypes) > 0 && isIn(paramTypes[0]) {\n\t\ttaggedParam := paramTypes[0]\n\t\tfields := []reflect.StructField{\n\t\t\ttaggedParam.Field(0),\n\t\t\t{\n\t\t\t\tName: \"Lifecycle\",\n\t\t\t\tType: _typeOfLifecycle,\n\t\t\t},\n\t\t}\n\t\tfor i := 1; i < taggedParam.NumField(); i++ {\n\t\t\tfields = append(fields, taggedParam.Field(i))\n\t\t}\n\t\tnewParamType := reflect.StructOf(fields)\n\t\treturn []reflect.Type{newParamType}, func(args []reflect.Value) []reflect.Value {\n\t\t\tparam := args[0]\n\t\t\targs[0] = reflect.New(taggedParam).Elem()\n\t\t\tfor i := 1; i < taggedParam.NumField(); i++ {\n\t\t\t\targs[0].Field(i).Set(param.Field(i + 1))\n\t\t\t}\n\t\t\treturn args\n\t\t}\n\t}\n\n\treturn append([]reflect.Type{_typeOfLifecycle}, paramTypes...), func(args []reflect.Value) []reflect.Value {\n\t\treturn args[1:]\n\t}\n}\n\nfunc lifecycleExists(paramTypes []reflect.Type) bool {\n\tfor _, t := range paramTypes {\n\t\tif t == _typeOfLifecycle {\n\t\t\treturn true\n\t\t}\n\t\tif isIn(t) {\n\t\t\tfor i := 1; i < t.NumField(); i++ {\n\t\t\t\tif t.Field(i).Type == _typeOfLifecycle {\n\t\t\t\t\treturn true\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn false\n}\n\nfunc (la *lifecycleHookAnnotation) buildHook(fn func(context.Context) error) (hook Hook) {\n\tswitch la.Type {\n\tcase _onStartHookType:\n\t\thook.OnStart = fn\n\tcase _onStopHookType:\n\t\thook.OnStop = fn\n\t}\n\treturn hook\n}\n\n// OnStart is an Annotation that appends an OnStart Hook to the application\n// Lifecycle when that function is called. This provides a way to create\n// Lifecycle OnStart (see Lifecycle type documentation) hooks without building a\n// function that takes a dependency on the Lifecycle type.\n//\n//\tfx.Provide(\n//\t\tfx.Annotate(\n//\t\t\tNewServer,\n//\t\t\tfx.OnStart(func(ctx context.Context, server Server) error {\n//\t\t\t\treturn server.Listen(ctx)\n//\t\t\t}),\n//\t\t)\n//\t)\n//\n// Which is functionally the same as:\n//\n//\t fx.Provide(\n//\t   func(lifecycle fx.Lifecycle, p Params) Server {\n//\t     server := NewServer(p)\n//\t     lifecycle.Append(fx.Hook{\n//\t\t      OnStart: func(ctx context.Context) error {\n//\t\t\t    return server.Listen(ctx)\n//\t\t      },\n//\t     })\n//\t\t return server\n//\t   }\n//\t )\n//\n// It is also possible to use OnStart annotation with other parameter and result\n// annotations, provided that the parameter of the function passed to OnStart\n// matches annotated parameters and results.\n//\n// For example, the following is possible:\n//\n//\tfx.Provide(\n//\t\tfx.Annotate(\n//\t\t\tfunc (a A) B {...},\n//\t\t\tfx.ParamTags(`name:\"A\"`),\n//\t\t\tfx.ResultTags(`name:\"B\"`),\n//\t\t\tfx.OnStart(func (p OnStartParams) {...}),\n//\t\t),\n//\t)\n//\n// As long as OnStartParams looks like the following and has no other dependencies\n// besides Context or Lifecycle:\n//\n//\ttype OnStartParams struct {\n//\t\tfx.In\n//\t\tFieldA A `name:\"A\"`\n//\t\tFieldB B `name:\"B\"`\n//\t}\n//\n// Only one OnStart annotation may be applied to a given function at a time,\n// however functions may be annotated with other types of lifecycle Hooks, such\n// as OnStop. The hook function passed into OnStart cannot take any arguments\n// outside of the annotated constructor's existing dependencies or results, except\n// a context.Context.\nfunc OnStart(onStart any) Annotation {\n\treturn &lifecycleHookAnnotation{\n\t\tType:   _onStartHookType,\n\t\tTarget: onStart,\n\t}\n}\n\n// OnStop is an Annotation that appends an OnStop Hook to the application\n// Lifecycle when that function is called. This provides a way to create\n// Lifecycle OnStop (see Lifecycle type documentation) hooks without building a\n// function that takes a dependency on the Lifecycle type.\n//\n//\tfx.Provide(\n//\t\tfx.Annotate(\n//\t\t\tNewServer,\n//\t\t\tfx.OnStop(func(ctx context.Context, server Server) error {\n//\t\t\t\treturn server.Shutdown(ctx)\n//\t\t\t}),\n//\t\t)\n//\t)\n//\n// Which is functionally the same as:\n//\n//\t fx.Provide(\n//\t   func(lifecycle fx.Lifecycle, p Params) Server {\n//\t     server := NewServer(p)\n//\t     lifecycle.Append(fx.Hook{\n//\t\t      OnStop: func(ctx context.Context) error {\n//\t\t\t    return server.Shutdown(ctx)\n//\t\t      },\n//\t     })\n//\t\t return server\n//\t   }\n//\t )\n//\n// It is also possible to use OnStop annotation with other parameter and result\n// annotations, provided that the parameter of the function passed to OnStop\n// matches annotated parameters and results.\n//\n// For example, the following is possible:\n//\n//\tfx.Provide(\n//\t\tfx.Annotate(\n//\t\t\tfunc (a A) B {...},\n//\t\t\tfx.ParamTags(`name:\"A\"`),\n//\t\t\tfx.ResultTags(`name:\"B\"`),\n//\t\t\tfx.OnStop(func (p OnStopParams) {...}),\n//\t\t),\n//\t)\n//\n// As long as OnStopParams looks like the following and has no other dependencies\n// besides Context or Lifecycle:\n//\n//\ttype OnStopParams struct {\n//\t\tfx.In\n//\t\tFieldA A `name:\"A\"`\n//\t\tFieldB B `name:\"B\"`\n//\t}\n//\n// Only one OnStop annotation may be applied to a given function at a time,\n// however functions may be annotated with other types of lifecycle Hooks, such\n// as OnStart. The hook function passed into OnStop cannot take any arguments\n// outside of the annotated constructor's existing dependencies or results, except\n// a context.Context.\nfunc OnStop(onStop any) Annotation {\n\treturn &lifecycleHookAnnotation{\n\t\tType:   _onStopHookType,\n\t\tTarget: onStop,\n\t}\n}\n\ntype asAnnotation struct {\n\ttargets []any\n\ttypes   []asType\n}\n\ntype asType struct {\n\tself bool\n\ttyp  reflect.Type // May be nil if self is true.\n}\n\nfunc (a asType) String() string {\n\tif a.self {\n\t\treturn \"self\"\n\t}\n\treturn a.typ.String()\n}\n\nfunc isOut(t reflect.Type) bool {\n\treturn (t.Kind() == reflect.Struct &&\n\t\tdig.IsOut(reflect.New(t).Elem().Interface()))\n}\n\nfunc isIn(t reflect.Type) bool {\n\treturn (t.Kind() == reflect.Struct &&\n\t\tdig.IsIn(reflect.New(t).Elem().Interface()))\n}\n\nvar _ Annotation = (*asAnnotation)(nil)\n\n// As is an Annotation that annotates the result of a function (i.e. a\n// constructor) to be provided as another interface.\n//\n// For example, the following code specifies that the return type of\n// bytes.NewBuffer (bytes.Buffer) should be provided as io.Writer type:\n//\n//\tfx.Provide(\n//\t  fx.Annotate(bytes.NewBuffer, fx.As(new(io.Writer)))\n//\t)\n//\n// In other words, the code above is equivalent to:\n//\n//\tfx.Provide(func() io.Writer {\n//\t  return bytes.NewBuffer()\n//\t  // provides io.Writer instead of *bytes.Buffer\n//\t})\n//\n// Note that the bytes.Buffer type is provided as an io.Writer type, so this\n// constructor does NOT provide both bytes.Buffer and io.Writer type; it just\n// provides io.Writer type.\n//\n// When multiple values are returned by the annotated function, each type\n// gets mapped to corresponding positional result of the annotated function.\n//\n// For example,\n//\n//\tfunc a() (bytes.Buffer, bytes.Buffer) {\n//\t  ...\n//\t}\n//\tfx.Provide(\n//\t  fx.Annotate(a, fx.As(new(io.Writer), new(io.Reader)))\n//\t)\n//\n// Is equivalent to,\n//\n//\tfx.Provide(func() (io.Writer, io.Reader) {\n//\t  w, r := a()\n//\t  return w, r\n//\t}\n//\n// As entirely replaces the default return types of a function. In order\n// to maintain the original return types when using As, see [Self].\n//\n// As annotation cannot be used in a function that returns an [Out] struct as a return type.\nfunc As(interfaces ...any) Annotation {\n\treturn &asAnnotation{targets: interfaces}\n}\n\n// Self returns a special value that can be passed to [As] to indicate\n// that a type should be provided as its original type, in addition to whatever other\n// types it gets provided as via other [As] annotations.\n//\n// For example,\n//\n//\tfx.Provide(\n//\t  fx.Annotate(\n//\t    bytes.NewBuffer,\n//\t    fx.As(new(io.Writer)),\n//\t    fx.As(fx.Self()),\n//\t  )\n//\t)\n//\n// Is equivalent to,\n//\n//\tfx.Provide(\n//\t  bytes.NewBuffer,\n//\t  func(b *bytes.Buffer) io.Writer {\n//\t    return b\n//\t  },\n//\t)\n//\n// in that it provides the same *bytes.Buffer instance\n// as both a *bytes.Buffer and an io.Writer.\nfunc Self() any {\n\treturn &self{}\n}\n\ntype self struct{}\n\nfunc (at *asAnnotation) apply(ann *annotated) error {\n\tat.types = make([]asType, len(at.targets))\n\tfor i, typ := range at.targets {\n\t\tif _, ok := typ.(*self); ok {\n\t\t\tat.types[i] = asType{self: true}\n\t\t\tcontinue\n\t\t}\n\t\tt := reflect.TypeOf(typ)\n\t\tif t.Kind() != reflect.Ptr || t.Elem().Kind() != reflect.Interface {\n\t\t\treturn fmt.Errorf(\"fx.As: argument must be a pointer to an interface: got %v\", t)\n\t\t}\n\t\tt = t.Elem()\n\t\tat.types[i] = asType{typ: t}\n\t}\n\n\tann.As = append(ann.As, at.types)\n\treturn nil\n}\n\n// build implements Annotation\nfunc (at *asAnnotation) build(ann *annotated) (any, error) {\n\tparamTypes := ann.currentParamTypes()\n\n\tresultTypes, remapResults, err := at.results(ann)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\torigFn := reflect.ValueOf(ann.Target)\n\tnewFnType := reflect.FuncOf(paramTypes, resultTypes, false)\n\tnewFn := reflect.MakeFunc(newFnType, func(args []reflect.Value) []reflect.Value {\n\t\tresults := origFn.Call(args)\n\t\treturn remapResults(results)\n\t})\n\treturn newFn.Interface(), nil\n}\n\nfunc (at *asAnnotation) results(ann *annotated) (\n\ttypes []reflect.Type,\n\tremap func([]reflect.Value) []reflect.Value,\n\terr error,\n) {\n\ttypes, hasError := ann.currentResultTypes()\n\tfields := []reflect.StructField{_outAnnotationField}\n\tif hasError {\n\t\ttypes = types[:len(types)-1]\n\t}\n\tresultFields, getResult := extractResultFields(types)\n\n\tfor i, f := range resultFields {\n\t\tt := f.Type\n\t\tfield := reflect.StructField{\n\t\t\tName: fmt.Sprintf(\"Field%d\", i),\n\t\t\tType: t,\n\t\t\tTag:  f.Tag,\n\t\t}\n\n\t\tif i >= len(at.types) || at.types[i].self {\n\t\t\tfields = append(fields, field)\n\t\t\tcontinue\n\t\t}\n\n\t\tif !t.Implements(at.types[i].typ) {\n\t\t\treturn nil, nil, fmt.Errorf(\"invalid fx.As: %v does not implement %v\", t, at.types[i])\n\t\t}\n\t\tfield.Type = at.types[i].typ\n\t\tfields = append(fields, field)\n\t}\n\tresType := reflect.StructOf(fields)\n\n\tvar outTypes []reflect.Type\n\toutTypes = append(types, resType)\n\tif hasError {\n\t\toutTypes = append(outTypes, _typeOfError)\n\t}\n\n\treturn outTypes, func(results []reflect.Value) []reflect.Value {\n\t\tvar (\n\t\t\toutErr     error\n\t\t\toutResults []reflect.Value\n\t\t)\n\n\t\tfor i, r := range results {\n\t\t\tif i == len(results)-1 && hasError {\n\t\t\t\t// If hasError and this is the last item,\n\t\t\t\t// we are guaranteed that this is an error\n\t\t\t\t// object.\n\t\t\t\tif err, _ := r.Interface().(error); err != nil {\n\t\t\t\t\toutErr = err\n\t\t\t\t}\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\toutResults = append(outResults, r)\n\t\t}\n\n\t\tnewOutResult := reflect.New(resType).Elem()\n\t\tfor i := 1; i < resType.NumField(); i++ {\n\t\t\tnewOutResult.Field(i).Set(getResult(i, results))\n\t\t}\n\t\toutResults = append(outResults, newOutResult)\n\n\t\tif hasError {\n\t\t\tif outErr != nil {\n\t\t\t\toutResults = append(outResults, reflect.ValueOf(outErr))\n\t\t\t} else {\n\t\t\t\toutResults = append(outResults, _nilError)\n\t\t\t}\n\t\t}\n\n\t\treturn outResults\n\t}, nil\n}\n\nfunc extractResultFields(types []reflect.Type) ([]reflect.StructField, func(int, []reflect.Value) reflect.Value) {\n\tvar resultFields []reflect.StructField\n\tif len(types) > 0 && isOut(types[0]) {\n\t\tfor i := 1; i < types[0].NumField(); i++ {\n\t\t\tresultFields = append(resultFields, types[0].Field(i))\n\t\t}\n\t\treturn resultFields, func(idx int, results []reflect.Value) reflect.Value {\n\t\t\treturn results[0].Field(idx)\n\t\t}\n\t}\n\tfor i, t := range types {\n\t\tif isOut(t) {\n\t\t\tcontinue\n\t\t}\n\t\tfield := reflect.StructField{\n\t\t\tName: fmt.Sprintf(\"Field%d\", i),\n\t\t\tType: t,\n\t\t}\n\t\tresultFields = append(resultFields, field)\n\t}\n\treturn resultFields, func(idx int, results []reflect.Value) reflect.Value {\n\t\treturn results[idx-1]\n\t}\n}\n\ntype fromAnnotation struct {\n\ttargets []any\n\ttypes   []reflect.Type\n}\n\nvar _ Annotation = (*fromAnnotation)(nil)\n\n// From is an [Annotation] that annotates the parameter(s) for a function (i.e. a\n// constructor) to be accepted from other provided types. It is analogous to the\n// [As] for parameter types to the constructor.\n//\n// For example,\n//\n//\ttype Runner interface { Run() }\n//\tfunc NewFooRunner() *FooRunner // implements Runner\n//\tfunc NewRunnerWrap(r Runner) *RunnerWrap\n//\n//\tfx.Provide(\n//\t  fx.Annotate(\n//\t    NewRunnerWrap,\n//\t    fx.From(new(*FooRunner)),\n//\t  ),\n//\t)\n//\n// Is equivalent to,\n//\n//\tfx.Provide(func(r *FooRunner) *RunnerWrap {\n//\t  // need *FooRunner instead of Runner\n//\t  return NewRunnerWrap(r)\n//\t})\n//\n// When the annotated function takes in multiple parameters, each type gets\n// mapped to corresponding positional parameter of the annotated function\n//\n// For example,\n//\n//\tfunc NewBarRunner() *BarRunner // implements Runner\n//\tfunc NewRunnerWraps(r1 Runner, r2 Runner) *RunnerWraps\n//\n//\tfx.Provide(\n//\t  fx.Annotate(\n//\t    NewRunnerWraps,\n//\t    fx.From(new(*FooRunner), new(*BarRunner)),\n//\t  ),\n//\t)\n//\n// Is equivalent to,\n//\n//\tfx.Provide(func(r1 *FooRunner, r2 *BarRunner) *RunnerWraps {\n//\t  return NewRunnerWraps(r1, r2)\n//\t})\n//\n// From annotation cannot be used in a function that takes an [In] struct as a\n// parameter.\nfunc From(interfaces ...any) Annotation {\n\treturn &fromAnnotation{targets: interfaces}\n}\n\nfunc (fr *fromAnnotation) apply(ann *annotated) error {\n\tif len(ann.From) > 0 {\n\t\treturn errors.New(\"cannot apply more than one line of From\")\n\t}\n\tft := reflect.TypeOf(ann.Target)\n\tfr.types = make([]reflect.Type, len(fr.targets))\n\tfor i, typ := range fr.targets {\n\t\tif ft.IsVariadic() && i == ft.NumIn()-1 {\n\t\t\treturn errors.New(\"fx.From: cannot annotate a variadic argument\")\n\t\t}\n\t\tt := reflect.TypeOf(typ)\n\t\tif t == nil || t.Kind() != reflect.Ptr {\n\t\t\treturn fmt.Errorf(\"fx.From: argument must be a pointer to a type that implements some interface: got %v\", t)\n\t\t}\n\t\tfr.types[i] = t.Elem()\n\t}\n\tann.From = fr.types\n\treturn nil\n}\n\n// build builds and returns a constructor after applying a From annotation\nfunc (fr *fromAnnotation) build(ann *annotated) (any, error) {\n\tparamTypes, remap, err := fr.parameters(ann)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tresultTypes, _ := ann.currentResultTypes()\n\n\torigFn := reflect.ValueOf(ann.Target)\n\tnewFnType := reflect.FuncOf(paramTypes, resultTypes, false)\n\tnewFn := reflect.MakeFunc(newFnType, func(args []reflect.Value) []reflect.Value {\n\t\targs = remap(args)\n\t\treturn origFn.Call(args)\n\t})\n\treturn newFn.Interface(), nil\n}\n\n// parameters returns the type for the parameters of the annotated function,\n// and a function that maps the arguments of the annotated function\n// back to the arguments of the target function.\nfunc (fr *fromAnnotation) parameters(ann *annotated) (\n\ttypes []reflect.Type,\n\tremap func([]reflect.Value) []reflect.Value,\n\terr error,\n) {\n\tft := reflect.TypeOf(ann.Target)\n\ttypes = make([]reflect.Type, ft.NumIn())\n\tfor i := 0; i < ft.NumIn(); i++ {\n\t\ttypes[i] = ft.In(i)\n\t}\n\n\t// No parameter annotations. Return the original types\n\t// and an identity function.\n\tif len(fr.targets) == 0 {\n\t\treturn types, func(args []reflect.Value) []reflect.Value {\n\t\t\treturn args\n\t\t}, nil\n\t}\n\n\t// Turn parameters into an fx.In struct.\n\tinFields := []reflect.StructField{_inAnnotationField}\n\n\t// The following situations may occur:\n\t// 1. there was a variadic argument, so it was pre-transformed.\n\t// 2. another parameter annotation was transformed (ex: ParamTags).\n\t// so need to visit fields of the fx.In struct.\n\tif len(types) > 0 && isIn(types[0]) {\n\t\tparamType := types[0]\n\n\t\tfor i := 1; i < paramType.NumField(); i++ {\n\t\t\torigField := paramType.Field(i)\n\t\t\tfield := reflect.StructField{\n\t\t\t\tName: origField.Name,\n\t\t\t\tType: origField.Type,\n\t\t\t\tTag:  origField.Tag,\n\t\t\t}\n\t\t\tif i-1 < len(fr.types) {\n\t\t\t\tt := fr.types[i-1]\n\t\t\t\tif !t.Implements(field.Type) {\n\t\t\t\t\treturn nil, nil, fmt.Errorf(\"invalid fx.From: %v does not implement %v\", t, field.Type)\n\t\t\t\t}\n\t\t\t\tfield.Type = t\n\t\t\t}\n\n\t\t\tinFields = append(inFields, field)\n\t\t}\n\n\t\ttypes = []reflect.Type{reflect.StructOf(inFields)}\n\t\treturn types, func(args []reflect.Value) []reflect.Value {\n\t\t\tparam := args[0]\n\t\t\targs[0] = reflect.New(paramType).Elem()\n\t\t\tfor i := 1; i < paramType.NumField(); i++ {\n\t\t\t\targs[0].Field(i).Set(param.Field(i))\n\t\t\t}\n\t\t\treturn args\n\t\t}, nil\n\t}\n\n\tfor i, t := range types {\n\t\tfield := reflect.StructField{\n\t\t\tName: fmt.Sprintf(\"Field%d\", i),\n\t\t\tType: t,\n\t\t}\n\t\tif i < len(fr.types) {\n\t\t\tt := fr.types[i]\n\t\t\tif !t.Implements(field.Type) {\n\t\t\t\treturn nil, nil, fmt.Errorf(\"invalid fx.From: %v does not implement %v\", t, field.Type)\n\t\t\t}\n\t\t\tfield.Type = t\n\t\t}\n\n\t\tinFields = append(inFields, field)\n\t}\n\n\ttypes = []reflect.Type{reflect.StructOf(inFields)}\n\treturn types, func(args []reflect.Value) []reflect.Value {\n\t\tparams := args[0]\n\t\targs = args[:0]\n\t\tfor i := 0; i < ft.NumIn(); i++ {\n\t\t\targs = append(args, params.Field(i+1))\n\t\t}\n\t\treturn args\n\t}, nil\n}\n\ntype annotated struct {\n\tTarget      any\n\tAnnotations []Annotation\n\tParamTags   []string\n\tResultTags  []string\n\tAs          [][]asType\n\tFrom        []reflect.Type\n\tFuncPtr     uintptr\n\tHooks       []*lifecycleHookAnnotation\n\t// container is used to build private scopes for lifecycle hook functions\n\t// added via fx.OnStart and fx.OnStop annotations.\n\tcontainer *dig.Container\n}\n\nfunc (ann annotated) String() string {\n\tvar sb strings.Builder\n\tsb.WriteString(\"fx.Annotate(\")\n\tsb.WriteString(fxreflect.FuncName(ann.Target))\n\tif tags := ann.ParamTags; len(tags) > 0 {\n\t\tfmt.Fprintf(&sb, \", fx.ParamTags(%q)\", tags)\n\t}\n\tif tags := ann.ResultTags; len(tags) > 0 {\n\t\tfmt.Fprintf(&sb, \", fx.ResultTags(%q)\", tags)\n\t}\n\tif as := ann.As; len(as) > 0 {\n\t\tfmt.Fprintf(&sb, \", fx.As(%v)\", as)\n\t}\n\tif from := ann.From; len(from) > 0 {\n\t\tfmt.Fprintf(&sb, \", fx.From(%v)\", from)\n\t}\n\treturn sb.String()\n}\n\n// Build builds and returns a constructor based on fx.In/fx.Out params and\n// results wrapping the original constructor passed to fx.Annotate.\nfunc (ann *annotated) Build() (any, error) {\n\tann.container = dig.New()\n\tft := reflect.TypeOf(ann.Target)\n\tif ft.Kind() != reflect.Func {\n\t\treturn nil, fmt.Errorf(\"must provide constructor function, got %v (%T)\", ann.Target, ann.Target)\n\t}\n\n\tif err := ann.typeCheckOrigFn(); err != nil {\n\t\treturn nil, fmt.Errorf(\"invalid annotation function %T: %w\", ann.Target, err)\n\t}\n\n\tann.applyOptionalTag()\n\n\tvar (\n\t\terr        error\n\t\tlcHookAnns []*lifecycleHookAnnotation\n\t)\n\tfor _, annotation := range ann.Annotations {\n\t\tif lcHookAnn, ok := annotation.(*lifecycleHookAnnotation); ok {\n\t\t\tlcHookAnns = append(lcHookAnns, lcHookAnn)\n\t\t\tcontinue\n\t\t}\n\t\tif ann.Target, err = annotation.build(ann); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\n\t// need to call cleanUpAsResults before applying lifecycle annotations\n\t// to exclude the original results from the hook's scope if any\n\t// fx.As annotations were applied\n\tann.cleanUpAsResults()\n\n\tfor _, la := range lcHookAnns {\n\t\tif ann.Target, err = la.build(ann); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\treturn ann.Target, nil\n}\n\n// applyOptionalTag checks if function being annotated is variadic\n// and applies optional tag to the variadic argument before\n// applying any other annotations\nfunc (ann *annotated) applyOptionalTag() {\n\tft := reflect.TypeOf(ann.Target)\n\tif !ft.IsVariadic() {\n\t\treturn\n\t}\n\n\tresultTypes, _ := ann.currentResultTypes()\n\n\tfields := []reflect.StructField{_inAnnotationField}\n\tfor i := 0; i < ft.NumIn(); i++ {\n\t\tfield := reflect.StructField{\n\t\t\tName: fmt.Sprintf(\"Field%d\", i),\n\t\t\tType: ft.In(i),\n\t\t}\n\t\tif i == ft.NumIn()-1 {\n\t\t\t// Mark a variadic argument optional by default\n\t\t\t// so that just wrapping a function in fx.Annotate does not\n\t\t\t// suddenly introduce a required []arg dependency.\n\t\t\tfield.Tag = reflect.StructTag(`optional:\"true\"`)\n\t\t}\n\t\tfields = append(fields, field)\n\t}\n\tparamType := reflect.StructOf(fields)\n\torigFn := reflect.ValueOf(ann.Target)\n\tnewFnType := reflect.FuncOf([]reflect.Type{paramType}, resultTypes, false)\n\tnewFn := reflect.MakeFunc(newFnType, func(args []reflect.Value) []reflect.Value {\n\t\tparams := args[0]\n\t\targs = args[:0]\n\t\tfor i := 0; i < ft.NumIn(); i++ {\n\t\t\targs = append(args, params.Field(i+1))\n\t\t}\n\t\treturn origFn.CallSlice(args)\n\t})\n\tann.Target = newFn.Interface()\n}\n\n// cleanUpAsResults does a check to see if an As annotation was applied.\n// If there was any fx.As annotation applied, cleanUpAsResults wraps the\n// function one more time to remove the results from the original function.\nfunc (ann *annotated) cleanUpAsResults() {\n\t// clean up orig function results if there were any As annotations\n\tif len(ann.As) < 1 {\n\t\treturn\n\t}\n\tparamTypes := ann.currentParamTypes()\n\tresultTypes, hasError := ann.currentResultTypes()\n\tnumRes := len(ann.As)\n\tif hasError {\n\t\tnumRes++\n\t}\n\tnewResultTypes := resultTypes[len(resultTypes)-numRes:]\n\torigFn := reflect.ValueOf(ann.Target)\n\tnewFnType := reflect.FuncOf(paramTypes, newResultTypes, false)\n\tnewFn := reflect.MakeFunc(newFnType, func(args []reflect.Value) (results []reflect.Value) {\n\t\tresults = origFn.Call(args)\n\t\tresults = results[len(results)-numRes:]\n\t\treturn\n\t})\n\tann.Target = newFn.Interface()\n}\n\n// checks and returns a non-nil error if the target function:\n// - returns an fx.Out struct as a result and has either a ResultTags or an As annotation\n// - takes in an fx.In struct as a parameter and has either a ParamTags or a From annotation\n// - has an error result not as the last result.\nfunc (ann *annotated) typeCheckOrigFn() error {\n\tft := reflect.TypeOf(ann.Target)\n\tnumOut := ft.NumOut()\n\tfor i := range numOut {\n\t\tot := ft.Out(i)\n\t\tif ot == _typeOfError && i != numOut-1 {\n\t\t\treturn fmt.Errorf(\n\t\t\t\t\"only the last result can be an error: \"+\n\t\t\t\t\t\"%v (%v) returns error as result %d\",\n\t\t\t\tfxreflect.FuncName(ann.Target), ft, i)\n\t\t}\n\t\tif ot.Kind() != reflect.Struct {\n\t\t\tcontinue\n\t\t}\n\t\tif !dig.IsOut(reflect.New(ft.Out(i)).Elem().Interface()) {\n\t\t\tcontinue\n\t\t}\n\t\tif len(ann.ResultTags) > 0 || len(ann.As) > 0 {\n\t\t\treturn errors.New(\"fx.Out structs cannot be annotated with fx.ResultTags or fx.As\")\n\t\t}\n\t}\n\tfor i := 0; i < ft.NumIn(); i++ {\n\t\tit := ft.In(i)\n\t\tif it.Kind() != reflect.Struct {\n\t\t\tcontinue\n\t\t}\n\t\tif !dig.IsIn(reflect.New(ft.In(i)).Elem().Interface()) {\n\t\t\tcontinue\n\t\t}\n\t\tif len(ann.ParamTags) > 0 || len(ann.From) > 0 {\n\t\t\treturn errors.New(\"fx.In structs cannot be annotated with fx.ParamTags or fx.From\")\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc (ann *annotated) currentResultTypes() (resultTypes []reflect.Type, hasError bool) {\n\tft := reflect.TypeOf(ann.Target)\n\tnumOut := ft.NumOut()\n\tresultTypes = make([]reflect.Type, numOut)\n\n\tfor i := range numOut {\n\t\tresultTypes[i] = ft.Out(i)\n\t\tif resultTypes[i] == _typeOfError && i == numOut-1 {\n\t\t\thasError = true\n\t\t}\n\t}\n\treturn resultTypes, hasError\n}\n\nfunc (ann *annotated) currentParamTypes() []reflect.Type {\n\tft := reflect.TypeOf(ann.Target)\n\tparamTypes := make([]reflect.Type, ft.NumIn())\n\n\tfor i := 0; i < ft.NumIn(); i++ {\n\t\tparamTypes[i] = ft.In(i)\n\t}\n\treturn paramTypes\n}\n\n// Annotate lets you annotate a function's parameters and returns\n// without you having to declare separate struct definitions for them.\n//\n// For example,\n//\n//\tfunc NewGateway(ro, rw *db.Conn) *Gateway { ... }\n//\tfx.Provide(\n//\t  fx.Annotate(\n//\t    NewGateway,\n//\t    fx.ParamTags(`name:\"ro\" optional:\"true\"`, `name:\"rw\"`),\n//\t    fx.ResultTags(`name:\"foo\"`),\n//\t  ),\n//\t)\n//\n// Is equivalent to,\n//\n//\ttype params struct {\n//\t  fx.In\n//\n//\t  RO *db.Conn `name:\"ro\" optional:\"true\"`\n//\t  RW *db.Conn `name:\"rw\"`\n//\t}\n//\n//\ttype result struct {\n//\t  fx.Out\n//\n//\t  GW *Gateway `name:\"foo\"`\n//\t}\n//\n//\tfx.Provide(func(p params) result {\n//\t   return result{GW: NewGateway(p.RO, p.RW)}\n//\t})\n//\n// Using the same annotation multiple times is invalid.\n// For example, the following will fail with an error:\n//\n//\tfx.Provide(\n//\t  fx.Annotate(\n//\t    NewGateWay,\n//\t    fx.ParamTags(`name:\"ro\" optional:\"true\"`),\n//\t    fx.ParamTags(`name:\"rw\"), // ERROR: ParamTags was already used above\n//\t    fx.ResultTags(`name:\"foo\"`)\n//\t  )\n//\t)\n//\n// If more tags are given than the number of parameters/results, only\n// the ones up to the number of parameters/results will be applied.\n//\n// # Variadic functions\n//\n// If the provided function is variadic, Annotate treats its parameter as a\n// slice. For example,\n//\n//\tfx.Annotate(func(w io.Writer, rs ...io.Reader) {\n//\t  // ...\n//\t}, ...)\n//\n// Is equivalent to,\n//\n//\tfx.Annotate(func(w io.Writer, rs []io.Reader) {\n//\t  // ...\n//\t}, ...)\n//\n// You can use variadic parameters with Fx's value groups.\n// For example,\n//\n//\tfx.Annotate(func(mux *http.ServeMux, handlers ...http.Handler) {\n//\t  // ...\n//\t}, fx.ParamTags(``, `group:\"server\"`))\n//\n// If we provide the above to the application,\n// any constructor in the Fx application can inject its HTTP handlers\n// by using [Annotate], [Annotated], or [Out].\n//\n//\tfx.Annotate(\n//\t  func(..) http.Handler { ... },\n//\t  fx.ResultTags(`group:\"server\"`),\n//\t)\n//\n//\tfx.Annotated{\n//\t  Target: func(..) http.Handler { ... },\n//\t  Group:  \"server\",\n//\t}\nfunc Annotate(t any, anns ...Annotation) any {\n\tresult := annotated{Target: t}\n\tfor _, ann := range anns {\n\t\tif err := ann.apply(&result); err != nil {\n\t\t\treturn annotationError{\n\t\t\t\ttarget: t,\n\t\t\t\terr:    err,\n\t\t\t}\n\t\t}\n\t}\n\tresult.Annotations = anns\n\treturn result\n}\n"
  },
  {
    "path": "annotated_test.go",
    "content": "// Copyright (c) 2019-2021 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage fx_test\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"strconv\"\n\t\"strings\"\n\t\"sync/atomic\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\t\"go.uber.org/fx\"\n\t\"go.uber.org/fx/fxevent\"\n\t\"go.uber.org/fx/fxtest\"\n)\n\nfunc TestAnnotated(t *testing.T) {\n\tt.Parallel()\n\n\ttype a struct {\n\t\tname string\n\t}\n\ttype in struct {\n\t\tfx.In\n\n\t\tA *a `name:\"foo\"`\n\t}\n\tnewA := func() *a {\n\t\treturn &a{name: \"foo\"}\n\t}\n\tt.Run(\"Provide\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tvar in in\n\t\tapp := fxtest.New(t,\n\t\t\tfx.Provide(\n\t\t\t\tfx.Annotated{\n\t\t\t\t\tName:   \"foo\",\n\t\t\t\t\tTarget: newA,\n\t\t\t\t},\n\t\t\t),\n\t\t\tfx.Populate(&in),\n\t\t)\n\t\tdefer app.RequireStart().RequireStop()\n\t\tassert.NotNil(t, in.A, \"expected in.A to be injected\")\n\t\tassert.Equal(t, \"foo\", in.A.name, \"expected to get a type 'a' of name 'foo'\")\n\t})\n}\n\ntype fromStringer struct {\n\tname string\n}\n\nfunc (from *fromStringer) String() string {\n\treturn from.name\n}\n\ntype asStringer struct {\n\tname string\n}\n\nfunc (as *asStringer) String() string {\n\treturn as.name\n}\n\ntype anotherStringer struct {\n\tname string\n}\n\nfunc (a anotherStringer) String() string {\n\treturn a.name\n}\n\nfunc TestAnnotatedFrom(t *testing.T) {\n\tt.Parallel()\n\ttype myStringer interface {\n\t\tString() string\n\t}\n\n\tnewFromStringer := func() *fromStringer {\n\t\treturn &fromStringer{\n\t\t\tname: \"a good stringer\",\n\t\t}\n\t}\n\n\ttests := []struct {\n\t\tdesc    string\n\t\tprovide fx.Option\n\t\tinvoke  any\n\t}{\n\t\t{\n\t\t\tdesc: \"provide a good stringer\",\n\t\t\tprovide: fx.Provide(\n\t\t\t\tnewFromStringer,\n\t\t\t\tfx.Annotate(\n\t\t\t\t\tfunc(myStringer myStringer) fmt.Stringer {\n\t\t\t\t\t\treturn &fromStringer{\n\t\t\t\t\t\t\tname: myStringer.String(),\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t\t\t\t\tfx.From(new(*fromStringer)),\n\t\t\t\t),\n\t\t\t),\n\t\t\tinvoke: func(s fmt.Stringer) {\n\t\t\t\tassert.Equal(t, s.String(), \"a good stringer\")\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tdesc: \"value type implementing interface\",\n\t\t\tprovide: fx.Provide(\n\t\t\t\tfunc() anotherStringer {\n\t\t\t\t\treturn anotherStringer{\n\t\t\t\t\t\t\"another stringer\",\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\tfx.Annotate(\n\t\t\t\t\tfunc(myStringer myStringer) fmt.Stringer {\n\t\t\t\t\t\treturn &fromStringer{\n\t\t\t\t\t\t\tname: myStringer.String(),\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t\t\t\t\tfx.From(new(anotherStringer)),\n\t\t\t\t),\n\t\t\t),\n\t\t\tinvoke: func(s fmt.Stringer) {\n\t\t\t\tassert.Equal(t, s.String(), \"another stringer\")\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tdesc: \"provide with multiple types From\",\n\t\t\tprovide: fx.Provide(\n\t\t\t\tnewFromStringer,\n\t\t\t\tfunc() anotherStringer {\n\t\t\t\t\treturn anotherStringer{\n\t\t\t\t\t\t\"another stringer\",\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\tfx.Annotate(\n\t\t\t\t\tfunc(myStringer1 myStringer, myStringer2 myStringer) fmt.Stringer {\n\t\t\t\t\t\treturn &fromStringer{\n\t\t\t\t\t\t\tname: myStringer1.String() + \" and \" + myStringer2.String(),\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t\t\t\t\tfx.From(new(anotherStringer), new(*fromStringer)),\n\t\t\t\t),\n\t\t\t),\n\t\t\tinvoke: func(s fmt.Stringer) {\n\t\t\t\tassert.Equal(t, s.String(), \"another stringer and a good stringer\")\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tdesc: \"provide from with param annotation\",\n\t\t\tprovide: fx.Provide(\n\t\t\t\tfx.Annotate(\n\t\t\t\t\tnewFromStringer,\n\t\t\t\t\tfx.ResultTags(`name:\"struct1\"`),\n\t\t\t\t),\n\t\t\t\tfx.Annotate(\n\t\t\t\t\tfunc(myStringer myStringer) fmt.Stringer {\n\t\t\t\t\t\treturn &fromStringer{\n\t\t\t\t\t\t\tname: myStringer.String(),\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t\t\t\t\tfx.From(new(*fromStringer)),\n\t\t\t\t\tfx.ParamTags(`name:\"struct1\"`),\n\t\t\t\t),\n\t\t\t),\n\t\t\tinvoke: func(s fmt.Stringer) {\n\t\t\t\tassert.Equal(t, s.String(), \"a good stringer\")\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\t// same as the test above, except now we annotate\n\t\t\t// it in a different order.\n\t\t\tdesc: \"provide from with param annotation, in different order\",\n\t\t\tprovide: fx.Provide(\n\t\t\t\tfx.Annotate(\n\t\t\t\t\tnewFromStringer,\n\t\t\t\t\tfx.ResultTags(`name:\"struct1\"`),\n\t\t\t\t),\n\t\t\t\tfx.Annotate(\n\t\t\t\t\tfunc(myStringer myStringer) fmt.Stringer {\n\t\t\t\t\t\treturn &fromStringer{\n\t\t\t\t\t\t\tname: myStringer.String(),\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t\t\t\t\tfx.ParamTags(`name:\"struct1\"`),\n\t\t\t\t\tfx.From(new(*fromStringer)),\n\t\t\t\t),\n\t\t\t),\n\t\t\tinvoke: func(s fmt.Stringer) {\n\t\t\t\tassert.Equal(t, s.String(), \"a good stringer\")\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tdesc: \"annotate fewer items than required for constructor\",\n\t\t\tprovide: fx.Provide(\n\t\t\t\tnewFromStringer,\n\t\t\t\tfunc() anotherStringer {\n\t\t\t\t\treturn anotherStringer{\n\t\t\t\t\t\t\"another stringer\",\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\tfx.Annotate(\n\t\t\t\t\tfunc(myStringer1 myStringer, fromStringer2 *fromStringer) fmt.Stringer {\n\t\t\t\t\t\treturn &fromStringer{\n\t\t\t\t\t\t\tname: myStringer1.String() + \" and \" + fromStringer2.String(),\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t\t\t\t\tfx.From(new(anotherStringer)),\n\t\t\t\t),\n\t\t\t),\n\t\t\tinvoke: func(s fmt.Stringer) {\n\t\t\t\tassert.Equal(t, s.String(), \"another stringer and a good stringer\")\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tdesc: \"Provide with empty From type\",\n\t\t\tprovide: fx.Provide(\n\t\t\t\tnewFromStringer,\n\t\t\t\tfx.Annotate(\n\t\t\t\t\tfunc(myStringer *fromStringer) fmt.Stringer {\n\t\t\t\t\t\treturn &fromStringer{\n\t\t\t\t\t\t\tname: myStringer.String(),\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t\t\t\t\tfx.From(),\n\t\t\t\t),\n\t\t\t),\n\t\t\tinvoke: func(s fmt.Stringer) {\n\t\t\t\tassert.Equal(t, s.String(), \"a good stringer\")\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tdesc: \"Provide with variadic function\",\n\t\t\tprovide: fx.Provide(\n\t\t\t\tnewFromStringer,\n\t\t\t\tfx.Annotate(\n\t\t\t\t\tfunc(myStringer myStringer, x ...int) fmt.Stringer {\n\t\t\t\t\t\treturn &fromStringer{\n\t\t\t\t\t\t\tname: myStringer.String(),\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t\t\t\t\tfx.From(new(*fromStringer)),\n\t\t\t\t),\n\t\t\t),\n\t\t\tinvoke: func(s fmt.Stringer) {\n\t\t\t\tassert.Equal(t, s.String(), \"a good stringer\")\n\t\t\t},\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.desc, func(t *testing.T) {\n\t\t\tt.Parallel()\n\n\t\t\tapp := NewForTest(t,\n\t\t\t\tfx.WithLogger(func() fxevent.Logger {\n\t\t\t\t\treturn fxtest.NewTestLogger(t)\n\t\t\t\t}),\n\t\t\t\ttt.provide,\n\t\t\t\tfx.Invoke(tt.invoke),\n\t\t\t)\n\t\t\trequire.NoError(t, app.Err())\n\t\t})\n\t}\n}\n\nfunc TestAnnotatedFromFailures(t *testing.T) {\n\tt.Parallel()\n\ttype myStringer interface {\n\t\tString() string\n\t}\n\n\tnewFromStringer := func() *fromStringer {\n\t\treturn &fromStringer{name: \"stringer\"}\n\t}\n\n\ttests := []struct {\n\t\tdesc          string\n\t\tprovide       fx.Option\n\t\tinvoke        any\n\t\terrorContains string\n\t}{\n\t\t{\n\t\t\tdesc: \"provide when an illegal type From\",\n\t\t\tprovide: fx.Provide(\n\t\t\t\tfx.Annotate(\n\t\t\t\t\tfunc(writer io.Writer) fmt.Stringer {\n\t\t\t\t\t\treturn &fromStringer{}\n\t\t\t\t\t},\n\t\t\t\t\tfx.From(new(*fromStringer)),\n\t\t\t\t),\n\t\t\t),\n\t\t\tinvoke: func(stringer fmt.Stringer) {\n\t\t\t\tfmt.Println(stringer.String())\n\t\t\t},\n\t\t\terrorContains: \"*fx_test.fromStringer does not implement io.Writer\",\n\t\t},\n\t\t{\n\t\t\tdesc: \"provide with variadic function and an illegal type From\",\n\t\t\tprovide: fx.Provide(\n\t\t\t\tfx.Annotate(\n\t\t\t\t\tfunc(writer io.Writer, x ...int) fmt.Stringer {\n\t\t\t\t\t\treturn &fromStringer{}\n\t\t\t\t\t},\n\t\t\t\t\tfx.From(new(*fromStringer)),\n\t\t\t\t),\n\t\t\t),\n\t\t\tinvoke: func(stringer fmt.Stringer) {\n\t\t\t\tfmt.Println(stringer.String())\n\t\t\t},\n\t\t\terrorContains: \"*fx_test.fromStringer does not implement io.Writer\",\n\t\t},\n\t\t{\n\t\t\tdesc: \"don't provide original type using From\",\n\t\t\tprovide: fx.Provide(\n\t\t\t\tfx.Annotate(\n\t\t\t\t\tfunc(myStringer myStringer) fmt.Stringer {\n\t\t\t\t\t\treturn &fromStringer{\n\t\t\t\t\t\t\tname: myStringer.String(),\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t\t\t\t\tfx.From(new(*fromStringer)),\n\t\t\t\t),\n\t\t\t),\n\t\t\tinvoke: func(stringer fmt.Stringer) {\n\t\t\t\tfmt.Println(stringer.String())\n\t\t\t},\n\t\t\terrorContains: \"missing type: *fx_test.fromStringer\",\n\t\t},\n\t\t{\n\t\t\tdesc: \"fail to provide with name annotation\",\n\t\t\tprovide: fx.Provide(\n\t\t\t\tfx.Annotate(\n\t\t\t\t\tnewFromStringer,\n\t\t\t\t),\n\t\t\t\tfx.Annotate(\n\t\t\t\t\tfunc(myStringer myStringer) fmt.Stringer {\n\t\t\t\t\t\treturn &fromStringer{\n\t\t\t\t\t\t\tname: myStringer.String(),\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t\t\t\t\tfx.From(new(*fromStringer)),\n\t\t\t\t\tfx.ParamTags(`name:\"struct1\"`),\n\t\t\t\t),\n\t\t\t),\n\t\t\tinvoke: func(s fmt.Stringer) {\n\t\t\t\tassert.Equal(t, s.String(), \"a good stringer\")\n\t\t\t},\n\t\t\terrorContains: `missing type: *fx_test.fromStringer[name=\"struct1\"]`,\n\t\t},\n\t\t{\n\t\t\tdesc: \"non-pointer argument to From\",\n\t\t\tprovide: fx.Provide(\n\t\t\t\tfx.Annotate(\n\t\t\t\t\tnewFromStringer,\n\t\t\t\t\tfx.From(\"foo\"),\n\t\t\t\t),\n\t\t\t),\n\t\t\terrorContains: \"argument must be a pointer\",\n\t\t},\n\t\t{\n\t\t\tdesc: \"multiple from annotations\",\n\t\t\tprovide: fx.Provide(\n\t\t\t\tfx.Annotate(\n\t\t\t\t\tnewFromStringer,\n\t\t\t\t\tfx.From(new(asStringer)),\n\t\t\t\t\tfx.From(new(asStringer)),\n\t\t\t\t),\n\t\t\t),\n\t\t\terrorContains: \"cannot apply more than one line of From\",\n\t\t},\n\t\t{\n\t\t\tdesc: \"variadic argument\",\n\t\t\tprovide: fx.Provide(\n\t\t\t\tfx.Annotate(\n\t\t\t\t\tfunc(ss ...myStringer) {},\n\t\t\t\t\tfx.From(new(asStringer)),\n\t\t\t\t),\n\t\t\t),\n\t\t\terrorContains: \"cannot annotate a variadic argument\",\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.desc, func(t *testing.T) {\n\t\t\tt.Parallel()\n\t\t\tapp := NewForTest(t,\n\t\t\t\tfx.WithLogger(func() fxevent.Logger {\n\t\t\t\t\treturn fxtest.NewTestLogger(t)\n\t\t\t\t}),\n\t\t\t\ttt.provide,\n\t\t\t\tfx.Invoke(tt.invoke),\n\t\t\t)\n\t\t\terr := app.Err()\n\t\t\trequire.Error(t, err)\n\t\t\tassert.Contains(t, err.Error(), tt.errorContains)\n\t\t})\n\t}\n}\n\nfunc TestAnnotatedAs(t *testing.T) {\n\tt.Parallel()\n\ttype in struct {\n\t\tfx.In\n\n\t\tS fmt.Stringer `name:\"goodStringer\"`\n\t}\n\ttype inSelf struct {\n\t\tfx.In\n\n\t\tS1 fmt.Stringer `name:\"goodStringer\"`\n\t\tS2 *asStringer  `name:\"goodStringer\"`\n\t}\n\ttype myStringer interface {\n\t\tString() string\n\t}\n\n\tnewAsStringer := func() *asStringer {\n\t\treturn &asStringer{\n\t\t\tname: \"a good stringer\",\n\t\t}\n\t}\n\n\ttests := []struct {\n\t\tdesc     string\n\t\tprovide  fx.Option\n\t\tinvoke   any\n\t\tstartApp bool\n\t}{\n\t\t{\n\t\t\tdesc: \"provide a good stringer\",\n\t\t\tprovide: fx.Provide(\n\t\t\t\tfx.Annotate(newAsStringer, fx.As(new(fmt.Stringer))),\n\t\t\t),\n\t\t\tinvoke: func(s fmt.Stringer) {\n\t\t\t\tassert.Equal(t, s.String(), \"a good stringer\")\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tdesc: \"value type implementing interface\",\n\t\t\tprovide: fx.Provide(\n\t\t\t\tfx.Annotate(func() anotherStringer {\n\t\t\t\t\treturn anotherStringer{\n\t\t\t\t\t\t\"another stringer\",\n\t\t\t\t\t}\n\t\t\t\t}, fx.As(new(fmt.Stringer))),\n\t\t\t),\n\t\t\tinvoke: func(s fmt.Stringer) {\n\t\t\t\tassert.Equal(t, s.String(), \"another stringer\")\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tdesc: \"provide with multiple types As\",\n\t\t\tprovide: fx.Provide(fx.Annotate(func() (*asStringer, *bytes.Buffer) {\n\t\t\t\tbuf := make([]byte, 1)\n\t\t\t\tb := bytes.NewBuffer(buf)\n\t\t\t\treturn &asStringer{name: \"stringer\"}, b\n\t\t\t}, fx.As(new(fmt.Stringer), new(io.Writer)))),\n\t\t\tinvoke: func(s fmt.Stringer, w io.Writer) {\n\t\t\t\tw.Write([]byte(s.String()))\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tdesc: \"provide as with result annotation\",\n\t\t\tprovide: fx.Provide(\n\t\t\t\tfx.Annotate(func() *asStringer {\n\t\t\t\t\treturn &asStringer{name: \"stringer\"}\n\t\t\t\t},\n\t\t\t\t\tfx.ResultTags(`name:\"goodStringer\"`),\n\t\t\t\t\tfx.As(new(fmt.Stringer))),\n\t\t\t),\n\t\t\tinvoke: func(i in) {\n\t\t\t\tassert.Equal(t, \"stringer\", i.S.String())\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\t// same as the test above, except now we annotate\n\t\t\t// it in a different order.\n\t\t\tdesc: \"provide as with result annotation, in different order\",\n\t\t\tprovide: fx.Provide(\n\t\t\t\tfx.Annotate(func() *asStringer {\n\t\t\t\t\treturn &asStringer{name: \"stringer\"}\n\t\t\t\t},\n\t\t\t\t\tfx.As(new(fmt.Stringer)),\n\t\t\t\t\tfx.ResultTags(`name:\"goodStringer\"`)),\n\t\t\t),\n\t\t\tinvoke: func(i in) {\n\t\t\t\tassert.Equal(t, \"stringer\", i.S.String())\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tdesc: \"provide as with result annotation with error\",\n\t\t\tprovide: fx.Provide(\n\t\t\t\tfx.Annotate(func() (*asStringer, error) {\n\t\t\t\t\treturn &asStringer{name: \"stringer\"}, nil\n\t\t\t\t},\n\t\t\t\t\tfx.ResultTags(`name:\"goodStringer\"`),\n\t\t\t\t\tfx.As(new(fmt.Stringer))),\n\t\t\t),\n\t\t\tinvoke: func(i in) {\n\t\t\t\tassert.Equal(t, \"stringer\", i.S.String())\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tdesc: \"provide as with result annotation in different order with error\",\n\t\t\tprovide: fx.Provide(\n\t\t\t\tfx.Annotate(func() (*asStringer, error) {\n\t\t\t\t\treturn &asStringer{name: \"stringer\"}, nil\n\t\t\t\t},\n\t\t\t\t\tfx.As(new(fmt.Stringer)),\n\t\t\t\t\tfx.ResultTags(`name:\"goodStringer\"`)),\n\t\t\t),\n\t\t\tinvoke: func(i in) {\n\t\t\t\tassert.Equal(t, \"stringer\", i.S.String())\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tdesc: \"provide multiple constructors annotated As\",\n\t\t\tprovide: fx.Provide(\n\t\t\t\tfx.Annotate(func() *asStringer {\n\t\t\t\t\treturn &asStringer{name: \"stringer\"}\n\t\t\t\t}, fx.As(new(fmt.Stringer))),\n\t\t\t\tfx.Annotate(func() *bytes.Buffer {\n\t\t\t\t\tbuf := make([]byte, 1)\n\t\t\t\t\treturn bytes.NewBuffer(buf)\n\t\t\t\t}, fx.As(new(io.Writer))),\n\t\t\t),\n\t\t\tinvoke: func(s fmt.Stringer, w io.Writer) {\n\t\t\t\tassert.Equal(t, \"stringer\", s.String())\n\t\t\t\t_, err := w.Write([]byte{1})\n\t\t\t\trequire.NoError(t, err)\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tdesc: \"provide the same provider as multiple types\",\n\t\t\tprovide: fx.Provide(\n\t\t\t\tfx.Annotate(newAsStringer, fx.As(new(fmt.Stringer))),\n\t\t\t\tfx.Annotate(newAsStringer, fx.As(new(myStringer))),\n\t\t\t),\n\t\t\tinvoke: func(s fmt.Stringer, ms myStringer) {\n\t\t\t\tassert.Equal(t, \"a good stringer\", s.String())\n\t\t\t\tassert.Equal(t, \"a good stringer\", ms.String())\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tdesc: \"annotate fx.Supply\",\n\t\t\tprovide: fx.Supply(\n\t\t\t\tfx.Annotate(&asStringer{\"foo\"}, fx.As(new(fmt.Stringer))),\n\t\t\t),\n\t\t\tinvoke: func(s fmt.Stringer) {\n\t\t\t\tassert.Equal(t, \"foo\", s.String())\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tdesc: \"annotate as many interfaces\",\n\t\t\tprovide: fx.Provide(\n\t\t\t\tfx.Annotate(func() (*asStringer, error) {\n\t\t\t\t\treturn &asStringer{name: \"stringer\"}, nil\n\t\t\t\t},\n\t\t\t\t\tfx.As(new(fmt.Stringer)),\n\t\t\t\t\tfx.As(new(myStringer)),\n\t\t\t\t\tfx.ResultTags(`name:\"stringer\"`)),\n\t\t\t),\n\t\t\tinvoke: fx.Annotate(\n\t\t\t\tfunc(\n\t\t\t\t\tS fmt.Stringer,\n\t\t\t\t\tMS myStringer,\n\t\t\t\t) {\n\t\t\t\t\tassert.Equal(t, \"stringer\", S.String())\n\t\t\t\t\tassert.Equal(t, \"stringer\", MS.String())\n\t\t\t\t}, fx.ParamTags(`name:\"stringer\"`, `name:\"stringer\"`),\n\t\t\t),\n\t\t},\n\t\t{\n\t\t\tdesc: \"annotate as many interfaces with both-annotated return values\",\n\t\t\tprovide: fx.Provide(\n\t\t\t\tfx.Annotate(func() (*asStringer, *bytes.Buffer) {\n\t\t\t\t\treturn &asStringer{name: \"stringer\"},\n\t\t\t\t\t\tbytes.NewBuffer(make([]byte, 1))\n\t\t\t\t},\n\t\t\t\t\tfx.As(new(fmt.Stringer), new(io.Reader)),\n\t\t\t\t\tfx.As(new(myStringer), new(io.Writer))),\n\t\t\t),\n\t\t\tinvoke: func(s fmt.Stringer, ms myStringer, r io.Reader, w io.Writer) {\n\t\t\t\tassert.Equal(t, \"stringer\", s.String())\n\t\t\t\tassert.Equal(t, \"stringer\", ms.String())\n\t\t\t\t_, err := w.Write([]byte(\".\"))\n\t\t\t\tassert.NoError(t, err)\n\t\t\t\tbuf := make([]byte, 1)\n\t\t\t\t_, err = r.Read(buf)\n\t\t\t\tassert.NoError(t, err)\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tdesc: \"annotate as many interfaces with different numbers of annotations\",\n\t\t\tprovide: fx.Provide(\n\t\t\t\tfx.Annotate(func() (*asStringer, *bytes.Buffer) {\n\t\t\t\t\treturn &asStringer{name: \"stringer\"},\n\t\t\t\t\t\tbytes.NewBuffer(make([]byte, 1))\n\t\t\t\t},\n\t\t\t\t\t// annotate both in here\n\t\t\t\t\tfx.As(new(fmt.Stringer), new(io.Writer)),\n\t\t\t\t\t// annotate just myStringer here\n\t\t\t\t\tfx.As(new(myStringer))),\n\t\t\t),\n\t\t\tinvoke: func(s fmt.Stringer, ms myStringer, w io.Writer) {\n\t\t\t\tassert.Equal(t, \"stringer\", s.String())\n\t\t\t\tassert.Equal(t, \"stringer\", ms.String())\n\t\t\t\t_, err := w.Write([]byte(\".\"))\n\t\t\t\tassert.NoError(t, err)\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tdesc: \"annotate many interfaces with varying annotation count and check original type\",\n\t\t\tprovide: fx.Provide(\n\t\t\t\tfx.Annotate(func() (*asStringer, *bytes.Buffer) {\n\t\t\t\t\treturn &asStringer{name: \"stringer\"},\n\t\t\t\t\t\tbytes.NewBuffer(make([]byte, 1))\n\t\t\t\t},\n\t\t\t\t\t// annotate just myStringer here\n\t\t\t\t\tfx.As(new(myStringer)),\n\t\t\t\t\t// annotate both in here\n\t\t\t\t\tfx.As(new(fmt.Stringer), new(io.Writer))),\n\t\t\t),\n\t\t\tinvoke: func(s fmt.Stringer, ms myStringer, buf *bytes.Buffer, w io.Writer) {\n\t\t\t\tassert.Equal(t, \"stringer\", s.String())\n\t\t\t\tassert.Equal(t, \"stringer\", ms.String())\n\t\t\t\t_, err := w.Write([]byte(\".\"))\n\t\t\t\tassert.NoError(t, err)\n\t\t\t\t_, err = buf.Write([]byte(\".\"))\n\t\t\t\tassert.NoError(t, err)\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tdesc: \"annotate fewer items than provided constructor\",\n\t\t\tprovide: fx.Provide(\n\t\t\t\tfx.Annotate(func() (*bytes.Buffer, *strings.Builder) {\n\t\t\t\t\ts := \"Hello\"\n\t\t\t\t\treturn bytes.NewBuffer([]byte(s)), &strings.Builder{}\n\t\t\t\t},\n\t\t\t\t\tfx.As(new(io.Reader))),\n\t\t\t),\n\t\t\tinvoke: func(r io.Reader) {\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tdesc: \"results annotated as are provided to hooks as annotated types\",\n\t\t\tprovide: fx.Provide(\n\t\t\t\tfx.Annotate(func() (*asStringer, *bytes.Buffer) {\n\t\t\t\t\treturn &asStringer{name: \"stringer\"},\n\t\t\t\t\t\tbytes.NewBuffer([]byte{})\n\t\t\t\t},\n\t\t\t\t\t// lifecycle hook added is able to receive results as annotated\n\t\t\t\t\tfx.OnStart(func(s fmt.Stringer, ms myStringer, buf *bytes.Buffer, w io.Writer) {\n\t\t\t\t\t\tassert.Equal(t, \"stringer\", s.String())\n\t\t\t\t\t\tassert.Equal(t, \"stringer\", ms.String())\n\t\t\t\t\t\t_, err := w.Write([]byte(\".\"))\n\t\t\t\t\t\tassert.NoError(t, err)\n\t\t\t\t\t\t_, err = buf.Write([]byte(\".\"))\n\t\t\t\t\t\tassert.NoError(t, err)\n\t\t\t\t\t}),\n\t\t\t\t\tfx.OnStop(func(buf *bytes.Buffer) {\n\t\t\t\t\t\tassert.Equal(t, \"....\", buf.String(), \"buffer should contain bytes written in Invoke func and OnStart hook\")\n\t\t\t\t\t}),\n\t\t\t\t\t// annotate just myStringer here\n\t\t\t\t\tfx.As(new(myStringer)),\n\t\t\t\t\t// annotate both in here\n\t\t\t\t\tfx.As(new(fmt.Stringer), new(io.Writer))),\n\t\t\t),\n\t\t\tinvoke: func(s fmt.Stringer, ms myStringer, buf *bytes.Buffer, w io.Writer) {\n\t\t\t\tassert.Equal(t, \"stringer\", s.String())\n\t\t\t\tassert.Equal(t, \"stringer\", ms.String())\n\t\t\t\t_, err := w.Write([]byte(\".\"))\n\t\t\t\tassert.NoError(t, err)\n\t\t\t\t_, err = buf.Write([]byte(\".\"))\n\t\t\t\tassert.NoError(t, err)\n\t\t\t},\n\t\t\tstartApp: true,\n\t\t},\n\t\t{\n\t\t\tdesc: \"self w other As annotations\",\n\t\t\tprovide: fx.Provide(\n\t\t\t\tfx.Annotate(\n\t\t\t\t\tfunc() *asStringer {\n\t\t\t\t\t\treturn &asStringer{name: \"stringer\"}\n\t\t\t\t\t},\n\t\t\t\t\tfx.As(fx.Self()),\n\t\t\t\t\tfx.As(new(fmt.Stringer)),\n\t\t\t\t),\n\t\t\t),\n\t\t\tinvoke: func(s fmt.Stringer, as *asStringer) {\n\t\t\t\tassert.Equal(t, \"stringer\", s.String())\n\t\t\t\tassert.Equal(t, \"stringer\", as.String())\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tdesc: \"self as one As target\",\n\t\t\tprovide: fx.Provide(\n\t\t\t\tfx.Annotate(\n\t\t\t\t\tfunc() (*asStringer, *bytes.Buffer) {\n\t\t\t\t\t\ts := &asStringer{name: \"stringer\"}\n\t\t\t\t\t\tb := &bytes.Buffer{}\n\t\t\t\t\t\treturn s, b\n\t\t\t\t\t},\n\t\t\t\t\tfx.As(fx.Self(), new(io.Writer)),\n\t\t\t\t),\n\t\t\t),\n\t\t\tinvoke: func(s *asStringer, w io.Writer) {\n\t\t\t\tassert.Equal(t, \"stringer\", s.String())\n\t\t\t\t_, err := w.Write([]byte(\".\"))\n\t\t\t\tassert.NoError(t, err)\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tdesc: \"two as, two self, four types\",\n\t\t\tprovide: fx.Provide(\n\t\t\t\tfx.Annotate(\n\t\t\t\t\tfunc() (*asStringer, *bytes.Buffer) {\n\t\t\t\t\t\ts := &asStringer{name: \"stringer\"}\n\t\t\t\t\t\tb := &bytes.Buffer{}\n\t\t\t\t\t\treturn s, b\n\t\t\t\t\t},\n\t\t\t\t\tfx.As(fx.Self(), new(io.Writer)),\n\t\t\t\t\tfx.As(new(fmt.Stringer)),\n\t\t\t\t),\n\t\t\t),\n\t\t\tinvoke: func(s1 *asStringer, s2 fmt.Stringer, b *bytes.Buffer, w io.Writer) {\n\t\t\t\tassert.Equal(t, \"stringer\", s1.String())\n\t\t\t\tassert.Equal(t, \"stringer\", s2.String())\n\t\t\t\t_, err := w.Write([]byte(\".\"))\n\t\t\t\tassert.NoError(t, err)\n\t\t\t\t_, err = b.Write([]byte(\".\"))\n\t\t\t\tassert.NoError(t, err)\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tdesc: \"self with lifecycle hook\",\n\t\t\tprovide: fx.Provide(\n\t\t\t\tfx.Annotate(\n\t\t\t\t\tfunc() *asStringer {\n\t\t\t\t\t\treturn &asStringer{name: \"stringer\"}\n\t\t\t\t\t},\n\t\t\t\t\tfx.As(fx.Self()),\n\t\t\t\t\tfx.As(new(fmt.Stringer)),\n\t\t\t\t\tfx.OnStart(func(s fmt.Stringer, as *asStringer) {\n\t\t\t\t\t\tassert.Equal(t, \"stringer\", s.String())\n\t\t\t\t\t\tassert.Equal(t, \"stringer\", as.String())\n\t\t\t\t\t}),\n\t\t\t\t),\n\t\t\t),\n\t\t\tinvoke: func(s fmt.Stringer, as *asStringer) {\n\t\t\t\tassert.Equal(t, \"stringer\", s.String())\n\t\t\t\tassert.Equal(t, \"stringer\", as.String())\n\t\t\t},\n\t\t\tstartApp: true,\n\t\t},\n\t\t{\n\t\t\tdesc: \"self with result tags\",\n\t\t\tprovide: fx.Provide(\n\t\t\t\tfx.Annotate(\n\t\t\t\t\tfunc() *asStringer {\n\t\t\t\t\t\treturn &asStringer{name: \"stringer\"}\n\t\t\t\t\t},\n\t\t\t\t\tfx.As(fx.Self()),\n\t\t\t\t\tfx.As(new(fmt.Stringer)),\n\t\t\t\t\tfx.ResultTags(`name:\"goodStringer\"`),\n\t\t\t\t),\n\t\t\t),\n\t\t\tinvoke: func(i inSelf) {\n\t\t\t\tassert.Equal(t, \"stringer\", i.S1.String())\n\t\t\t\tassert.Equal(t, \"stringer\", i.S2.String())\n\t\t\t},\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.desc, func(t *testing.T) {\n\t\t\tt.Parallel()\n\n\t\t\tapp := NewForTest(t,\n\t\t\t\tfx.WithLogger(func() fxevent.Logger {\n\t\t\t\t\treturn fxtest.NewTestLogger(t)\n\t\t\t\t}),\n\t\t\t\ttt.provide,\n\t\t\t\tfx.Invoke(tt.invoke),\n\t\t\t)\n\t\t\trequire.NoError(t, app.Err())\n\t\t\tif tt.startApp {\n\t\t\t\tctx := context.Background()\n\t\t\t\trequire.NoError(t, app.Start(ctx))\n\t\t\t\trequire.NoError(t, app.Stop(ctx))\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestAnnotatedAsFailures(t *testing.T) {\n\tt.Parallel()\n\n\tnewAsStringer := func() *asStringer {\n\t\treturn &asStringer{name: \"stringer\"}\n\t}\n\n\tnewAsStringerWithErr := func() (*asStringer, error) {\n\t\treturn nil, errors.New(\"great sadness\")\n\t}\n\n\ttests := []struct {\n\t\tdesc          string\n\t\tprovide       fx.Option\n\t\tinvoke        any\n\t\terrorContains string\n\t}{\n\t\t{\n\t\t\tdesc:          \"provide when an illegal type As\",\n\t\t\tprovide:       fx.Provide(fx.Annotate(newAsStringer, fx.As(new(io.Writer)))),\n\t\t\tinvoke:        func() {},\n\t\t\terrorContains: \"asStringer does not implement io.Writer\",\n\t\t},\n\t\t{\n\t\t\tdesc:          \"provide when an illegal type As with result tag\",\n\t\t\tprovide:       fx.Provide(fx.Annotate(newAsStringer, fx.ResultTags(`name:\"stringer\"`), fx.As(new(io.Writer)))),\n\t\t\tinvoke:        func() {},\n\t\t\terrorContains: \"asStringer does not implement io.Writer\",\n\t\t},\n\t\t{\n\t\t\tdesc:          \"error is propagated without result tag\",\n\t\t\tprovide:       fx.Provide(fx.Annotate(newAsStringerWithErr, fx.As(new(fmt.Stringer)))),\n\t\t\tinvoke:        func(_ fmt.Stringer) {},\n\t\t\terrorContains: \"great sadness\",\n\t\t},\n\t\t{\n\t\t\tdesc:          \"error is propagated with result tag\",\n\t\t\tprovide:       fx.Provide(fx.Annotate(newAsStringerWithErr, fx.ResultTags(`name:\"stringer\"`), fx.As(new(fmt.Stringer)))),\n\t\t\tinvoke:        fx.Annotate(func(_ fmt.Stringer) {}, fx.ParamTags(`name:\"stringer\"`)),\n\t\t\terrorContains: \"great sadness\",\n\t\t},\n\t\t{\n\t\t\tdesc:    \"don't provide original type using As\",\n\t\t\tprovide: fx.Provide(fx.Annotate(newAsStringer, fx.As(new(fmt.Stringer)))),\n\t\t\tinvoke: func(as *asStringer) {\n\t\t\t\tfmt.Println(as.String())\n\t\t\t},\n\t\t\terrorContains: \"missing type: *fx_test.asStringer\",\n\t\t},\n\t\t{\n\t\t\tdesc: \"fail to provide with name annotation\",\n\t\t\tprovide: fx.Provide(fx.Annotate(func(n string) *asStringer {\n\t\t\t\treturn &asStringer{name: n}\n\t\t\t}, fx.As(new(fmt.Stringer)), fx.ParamTags(`name:\"n\"`))),\n\t\t\tinvoke: func(a fmt.Stringer) {\n\t\t\t\tfmt.Println(a)\n\t\t\t},\n\t\t\terrorContains: `missing type: string[name=\"n\"]`,\n\t\t},\n\t\t{\n\t\t\tdesc: \"non-pointer argument to As\",\n\t\t\tprovide: fx.Provide(\n\t\t\t\tfx.Annotate(\n\t\t\t\t\tnewAsStringer,\n\t\t\t\t\tfx.As(\"foo\"),\n\t\t\t\t),\n\t\t\t),\n\t\t\terrorContains: \"argument must be a pointer to an interface: got string\",\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.desc, func(t *testing.T) {\n\t\t\tt.Parallel()\n\t\t\tapp := NewForTest(t,\n\t\t\t\tfx.WithLogger(func() fxevent.Logger {\n\t\t\t\t\treturn fxtest.NewTestLogger(t)\n\t\t\t\t}),\n\t\t\t\ttt.provide,\n\t\t\t\tfx.Invoke(tt.invoke),\n\t\t\t)\n\t\t\terr := app.Err()\n\t\t\trequire.Error(t, err)\n\t\t\tassert.Contains(t, err.Error(), tt.errorContains)\n\t\t})\n\t}\n}\n\nfunc TestAnnotatedWrongUsage(t *testing.T) {\n\tt.Parallel()\n\n\ttype a struct {\n\t\tname string\n\t}\n\ttype in struct {\n\t\tfx.In\n\n\t\tA *a `name:\"foo\"`\n\t}\n\tnewA := func() *a {\n\t\treturn &a{name: \"foo\"}\n\t}\n\n\tt.Run(\"In Constructor\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tvar in in\n\t\tapp := NewForTest(t,\n\t\t\tfx.WithLogger(func() fxevent.Logger {\n\t\t\t\treturn fxtest.NewTestLogger(t)\n\t\t\t}),\n\t\t\tfx.Provide(\n\t\t\t\tfunc() fx.Annotated {\n\t\t\t\t\treturn fx.Annotated{\n\t\t\t\t\t\tName:   \"foo\",\n\t\t\t\t\t\tTarget: newA,\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t),\n\t\t\tfx.Populate(&in),\n\t\t)\n\n\t\terr := app.Err()\n\t\trequire.Error(t, err)\n\n\t\t// Example:\n\t\t// fx.Annotated should be passed to fx.Provide directly, it should not be returned by the constructor: fx.Provide received go.uber.org/fx_test.TestAnnotatedWrongUsage.func2.1() from:\n\t\t// go.uber.org/fx_test.TestAnnotatedWrongUsage.func2\n\t\t//         /.../fx/annotated_test.go:76\n\t\t// testing.tRunner\n\t\t//         /.../go/1.13.3/libexec/src/testing/testing.go:909\n\t\tassert.Contains(t, err.Error(), \"fx.Annotated should be passed to fx.Provide directly, it should not be returned by the constructor\")\n\t\tassert.Contains(t, err.Error(), \"fx.Provide received go.uber.org/fx_test.TestAnnotatedWrongUsage\")\n\t\tassert.Contains(t, err.Error(), \"go.uber.org/fx_test.TestAnnotatedWrongUsage\")\n\t\tassert.Contains(t, err.Error(), \"/annotated_test.go\")\n\t})\n\n\tt.Run(\"Result Type\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tapp := NewForTest(t,\n\t\t\tfx.WithLogger(func() fxevent.Logger {\n\t\t\t\treturn fxtest.NewTestLogger(t)\n\t\t\t}),\n\t\t\tfx.Provide(\n\t\t\t\tfx.Annotated{\n\t\t\t\t\tName: \"foo\",\n\t\t\t\t\tTarget: func() in {\n\t\t\t\t\t\treturn in{A: &a{name: \"foo\"}}\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t),\n\t\t)\n\t\tassert.Contains(t, app.Err().Error(), \"embeds a dig.In\", \"expected error when result types were annotated\")\n\t})\n\n\tt.Run(\"invalid group option\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tapp := NewForTest(t,\n\t\t\tfx.Provide(\n\t\t\t\tfx.Annotate(func() string { return \"sad times\" },\n\t\t\t\t\tfx.ResultTags(`group:\"foo,soft\"`)),\n\t\t\t),\n\t\t)\n\t\tassert.Contains(t, app.Err().Error(), \"cannot use soft with result value groups\", \"expected error when invalid group option is provided\")\n\t})\n}\n\nfunc TestAnnotatedString(t *testing.T) {\n\tt.Parallel()\n\n\ttests := []struct {\n\t\tdesc string\n\t\tgive fx.Annotated\n\t\twant string\n\t}{\n\t\t{\n\t\t\tdesc: \"empty\",\n\t\t\tgive: fx.Annotated{},\n\t\t\twant: \"fx.Annotated{}\",\n\t\t},\n\t\t{\n\t\t\tdesc: \"name\",\n\t\t\tgive: fx.Annotated{Name: \"foo\"},\n\t\t\twant: `fx.Annotated{Name: \"foo\"}`,\n\t\t},\n\t\t{\n\t\t\tdesc: \"group\",\n\t\t\tgive: fx.Annotated{Group: \"foo\"},\n\t\t\twant: `fx.Annotated{Group: \"foo\"}`,\n\t\t},\n\t\t{\n\t\t\tdesc: \"name and group\",\n\t\t\tgive: fx.Annotated{Name: \"foo\", Group: \"bar\"},\n\t\t\twant: `fx.Annotated{Name: \"foo\", Group: \"bar\"}`,\n\t\t},\n\t\t{\n\t\t\tdesc: \"target\",\n\t\t\tgive: fx.Annotated{Target: func() {}},\n\t\t\twant: \"fx.Annotated{Target: go.uber.org/fx_test.TestAnnotatedString.func1()}\",\n\t\t},\n\t\t{\n\t\t\tdesc: \"name and target\",\n\t\t\tgive: fx.Annotated{Name: \"foo\", Target: func() {}},\n\t\t\twant: `fx.Annotated{Name: \"foo\", Target: go.uber.org/fx_test.TestAnnotatedString.func2()}`,\n\t\t},\n\t\t{\n\t\t\tdesc: \"group and target\",\n\t\t\tgive: fx.Annotated{Group: \"foo\", Target: func() {}},\n\t\t\twant: `fx.Annotated{Group: \"foo\", Target: go.uber.org/fx_test.TestAnnotatedString.func3()}`,\n\t\t},\n\t\t{\n\t\t\tdesc: \"name, group and target\",\n\t\t\tgive: fx.Annotated{Name: \"foo\", Group: \"bar\", Target: func() {}},\n\t\t\twant: `fx.Annotated{Name: \"foo\", Group: \"bar\", Target: go.uber.org/fx_test.TestAnnotatedString.func4()}`,\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.desc, func(t *testing.T) {\n\t\t\tt.Parallel()\n\n\t\t\tassert.Equal(t, tt.want, tt.give.String())\n\t\t})\n\t}\n}\n\nfunc TestAnnotate(t *testing.T) {\n\tt.Parallel()\n\n\ttype a struct{}\n\ttype b struct{ a *a }\n\ttype c struct{ b *b }\n\ttype sliceA struct{ sa []*a }\n\tnewA := func() *a { return &a{} }\n\tnewB := func(a *a) *b {\n\t\treturn &b{a}\n\t}\n\tnewC := func(b *b) *c {\n\t\treturn &c{b}\n\t}\n\tnewSliceA := func(sa ...*a) *sliceA {\n\t\treturn &sliceA{sa}\n\t}\n\tnewSliceAWithB := func(b *b, sa ...*a) *sliceA {\n\t\ttotal := append(sa, b.a)\n\t\treturn &sliceA{total}\n\t}\n\n\tt.Run(\"Provide with empty param+result tags\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tapp := fxtest.New(t,\n\t\t\tfx.Provide(\n\t\t\t\tnewA,\n\t\t\t\tfx.Annotate(newB, fx.ParamTags(), fx.ResultTags()),\n\t\t\t),\n\t\t\tfx.Invoke(newC),\n\t\t)\n\t\tdefer app.RequireStart().RequireStop()\n\t\trequire.NoError(t, app.Err())\n\t})\n\n\tt.Run(\"Provide with optional\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tapp := fxtest.New(t,\n\t\t\tfx.Provide(\n\t\t\t\tfx.Annotate(newB, fx.ParamTags(`name:\"a\" optional:\"true\"`)),\n\t\t\t),\n\t\t\tfx.Invoke(newC),\n\t\t)\n\t\tdefer app.RequireStart().RequireStop()\n\t\trequire.NoError(t, app.Err())\n\t})\n\n\tt.Run(\"Provide with many annotated params\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tapp := fxtest.New(t,\n\t\t\tfx.Provide(\n\t\t\t\tfx.Annotate(newB, fx.ParamTags(`optional:\"true\"`)),\n\t\t\t\tfx.Annotate(func(a *a, b *b) any { return nil },\n\t\t\t\t\tfx.ParamTags(`name:\"a\" optional:\"true\"`, `name:\"b\"`),\n\t\t\t\t\tfx.ResultTags(`name:\"nil\"`),\n\t\t\t\t),\n\t\t\t),\n\t\t\tfx.Invoke(newC),\n\t\t)\n\t\tdefer app.RequireStart().RequireStop()\n\t\trequire.NoError(t, app.Err())\n\t})\n\n\tt.Run(\"Invoke with optional\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tapp := NewForTest(t,\n\t\t\tfx.Invoke(\n\t\t\t\tfx.Annotate(newB, fx.ParamTags(`optional:\"true\"`)),\n\t\t\t),\n\t\t)\n\t\terr := app.Err()\n\t\trequire.NoError(t, err)\n\t})\n\n\tt.Run(\"Invoke with a missing dependency\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tapp := NewForTest(t,\n\t\t\tfx.Invoke(\n\t\t\t\tfx.Annotate(newB, fx.ParamTags(`name:\"a\"`)),\n\t\t\t),\n\t\t)\n\t\terr := app.Err()\n\t\trequire.Error(t, err)\n\t\tassert.Contains(t, err.Error(), `missing dependencies`)\n\t\tassert.Contains(t, err.Error(), `missing type: *fx_test.a[name=\"a\"]`)\n\t})\n\n\tt.Run(\"Provide with variadic function\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tvar got *sliceA\n\t\tapp := fxtest.New(t,\n\t\t\tfx.Provide(\n\t\t\t\tfx.Annotated{Group: \"as\", Target: newA},\n\t\t\t\tfx.Annotated{Group: \"as\", Target: newA},\n\t\t\t\tfx.Annotate(newSliceA,\n\t\t\t\t\tfx.ParamTags(`group:\"as\"`),\n\t\t\t\t),\n\t\t\t),\n\t\t\tfx.Populate(&got),\n\t\t)\n\t\tdefer app.RequireStart().RequireStop()\n\t\trequire.NoError(t, app.Err())\n\n\t\tassert.Len(t, got.sa, 2)\n\t})\n\n\tt.Run(\"Provide variadic function with no optional params\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tvar got struct {\n\t\t\tfx.In\n\n\t\t\tResult *sliceA `name:\"as\"`\n\t\t}\n\t\tapp := fxtest.New(t,\n\t\t\tfx.Supply([]*a{{}, {}, {}}),\n\t\t\tfx.Provide(\n\t\t\t\tfx.Annotate(newSliceA,\n\t\t\t\t\tfx.ResultTags(`name:\"as\"`),\n\t\t\t\t),\n\t\t\t),\n\t\t\tfx.Populate(&got),\n\t\t)\n\t\tdefer app.RequireStart().RequireStop()\n\t\trequire.NoError(t, app.Err())\n\t\tassert.Len(t, got.Result.sa, 3)\n\t})\n\n\tt.Run(\"Provide variadic function named with no given params\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tvar got *sliceA\n\t\tapp := NewForTest(t,\n\t\t\tfx.Provide(\n\t\t\t\tfx.Annotate(newSliceA, fx.ParamTags(`name:\"a\"`)),\n\t\t\t),\n\t\t\tfx.Populate(&got),\n\t\t)\n\t\terr := app.Err()\n\t\trequire.Error(t, err)\n\t\tassert.Contains(t, err.Error(), `missing dependencies`)\n\t\tassert.Contains(t, err.Error(), `missing type: []*fx_test.a[name=\"a\"]`)\n\t})\n\n\tt.Run(\"Invoke function with soft group param\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tnewF := func(foos []int, bar string) {\n\t\t\tassert.ElementsMatch(t, []int{10}, foos)\n\t\t}\n\t\tapp := fxtest.New(t,\n\t\t\tfx.Provide(\n\t\t\t\tfx.Annotate(\n\t\t\t\t\tfunc() (int, string) { return 10, \"hello\" },\n\t\t\t\t\tfx.ResultTags(`group:\"foos\"`),\n\t\t\t\t),\n\t\t\t\tfx.Annotate(\n\t\t\t\t\tfunc() int {\n\t\t\t\t\t\trequire.FailNow(t, \"this function should not be called\")\n\t\t\t\t\t\treturn 20\n\t\t\t\t\t},\n\t\t\t\t\tfx.ResultTags(`group:\"foos\"`),\n\t\t\t\t),\n\t\t\t),\n\t\t\tfx.Invoke(\n\t\t\t\tfx.Annotate(newF, fx.ParamTags(`group:\"foos,soft\"`)),\n\t\t\t),\n\t\t)\n\n\t\tdefer app.RequireStart().RequireStop()\n\t\trequire.NoError(t, app.Err())\n\t})\n\n\tt.Run(\"Invoke variadic function with multiple params\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tapp := fxtest.New(t,\n\t\t\tfx.Supply(\n\t\t\t\tfx.Annotate(newB(newA()), fx.ResultTags(`name:\"b\"`)),\n\t\t\t),\n\t\t\tfx.Invoke(fx.Annotate(newSliceAWithB, fx.ParamTags(`name:\"b\"`))),\n\t\t)\n\n\t\tdefer app.RequireStart().RequireStop()\n\t\trequire.NoError(t, app.Err())\n\t})\n\n\tt.Run(\"Invoke non-optional variadic function with a missing dependency\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tapp := NewForTest(t,\n\t\t\tfx.Invoke(\n\t\t\t\tfx.Annotate(newSliceA, fx.ParamTags(`optional:\"false\"`)),\n\t\t\t),\n\t\t)\n\t\terr := app.Err()\n\t\trequire.Error(t, err)\n\t\tassert.Contains(t, err.Error(), `missing dependencies`)\n\t\tassert.Contains(t, err.Error(), `missing type: []*fx_test.a`)\n\t})\n\n\tt.Run(\"Invoke with variadic function\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\ttype T1 struct{ s string }\n\n\t\tapp := fxtest.New(t,\n\t\t\tfx.Supply(\n\t\t\t\tfx.Annotate(T1{\"foo\"}, fx.ResultTags(`group:\"t\"`)),\n\t\t\t\tfx.Annotate(T1{\"bar\"}, fx.ResultTags(`group:\"t\"`)),\n\t\t\t\tfx.Annotate(T1{\"baz\"}, fx.ResultTags(`group:\"t\"`)),\n\t\t\t),\n\t\t\tfx.Invoke(fx.Annotate(func(got ...T1) {\n\t\t\t\tassert.ElementsMatch(t, []T1{{\"foo\"}, {\"bar\"}, {\"baz\"}}, got)\n\t\t\t}, fx.ParamTags(`group:\"t\"`))),\n\t\t)\n\n\t\tdefer app.RequireStart().RequireStop()\n\t\trequire.NoError(t, app.Err())\n\t})\n\n\tt.Run(\"provide with annotated results\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tapp := fxtest.New(t,\n\t\t\tfx.Provide(\n\t\t\t\tfx.Annotate(func() *a {\n\t\t\t\t\treturn &a{}\n\t\t\t\t}, fx.ResultTags(`name:\"firstA\"`)),\n\t\t\t\tfx.Annotate(func() *a {\n\t\t\t\t\treturn &a{}\n\t\t\t\t}, fx.ResultTags(`name:\"secondA\"`)),\n\t\t\t\tfx.Annotate(func() *a {\n\t\t\t\t\treturn &a{}\n\t\t\t\t}, fx.ResultTags(`name:\"thirdA\"`)),\n\t\t\t\tfx.Annotate(func(a1 *a, a2 *a, a3 *a) *b {\n\t\t\t\t\treturn &b{a1}\n\t\t\t\t}, fx.ParamTags(\n\t\t\t\t\t`name:\"firstA\"`,\n\t\t\t\t\t`name:\"secondA\"`,\n\t\t\t\t\t`name:\"thirdA\"`,\n\t\t\t\t)),\n\t\t\t),\n\t\t\tfx.Invoke(newC),\n\t\t)\n\n\t\trequire.NoError(t, app.Err())\n\t\tdefer app.RequireStart().RequireStop()\n\t})\n\n\tt.Run(\"provide with missing annotated results\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tapp := NewForTest(t,\n\t\t\tfx.Provide(\n\t\t\t\tfx.Annotate(func() *a {\n\t\t\t\t\treturn &a{}\n\t\t\t\t}, fx.ResultTags(`name:\"firstA\"`)),\n\t\t\t\tfx.Annotate(func() *a {\n\t\t\t\t\treturn &a{}\n\t\t\t\t}, fx.ResultTags(`name:\"secondA\"`)),\n\t\t\t\tfx.Annotate(func() *a {\n\t\t\t\t\treturn &a{}\n\t\t\t\t}, fx.ResultTags(`name:\"fourthA\"`)),\n\t\t\t),\n\t\t\tfx.Invoke(\n\t\t\t\tfx.Annotate(func(a1 *a, a2 *a, a3 *a) *b {\n\t\t\t\t\treturn &b{a1}\n\t\t\t\t}, fx.ParamTags(\n\t\t\t\t\t`name:\"firstA\"`,\n\t\t\t\t\t`name:\"secondA\"`,\n\t\t\t\t\t`name:\"thirdA\"`)),\n\t\t\t),\n\t\t)\n\t\terr := app.Err()\n\t\trequire.Error(t, err)\n\t\tassert.Contains(t, err.Error(), `missing type: *fx_test.a[name=\"thirdA\"]`)\n\t})\n\n\tt.Run(\"error in the middle of a function\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tapp := NewForTest(t,\n\t\t\tfx.Provide(\n\t\t\t\tfx.Annotate(func() (*a, error, *a) { //nolint:staticcheck // we want to test error in the middle.\n\t\t\t\t\treturn &a{}, nil, &a{}\n\t\t\t\t}, fx.ResultTags(`name:\"firstA\"`, ``, `name:\"secondA\"`)),\n\t\t\t),\n\t\t\tfx.Invoke(\n\t\t\t\tfx.Annotate(func(*a) {}, fx.ParamTags(`name:\"firstA\"`)),\n\t\t\t),\n\t\t)\n\t\terr := app.Err()\n\t\trequire.Error(t, err)\n\t\tassert.Contains(t, err.Error(), \"only the last result can be an error\")\n\t\tassert.Contains(t, err.Error(), \"returns error as result 1\")\n\t})\n\n\tt.Run(\"provide with annotated results with error\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tapp := fxtest.New(t,\n\t\t\tfx.Provide(\n\t\t\t\tfx.Annotate(func() (*a, *a, error) {\n\t\t\t\t\treturn &a{}, &a{}, nil\n\t\t\t\t}, fx.ResultTags(`name:\"firstA\"`, `name:\"secondA\"`)),\n\t\t\t\tfx.Annotate(func() (*a, error) {\n\t\t\t\t\treturn &a{}, nil\n\t\t\t\t}, fx.ResultTags(`name:\"thirdA\"`)),\n\t\t\t),\n\t\t\tfx.Invoke(fx.Annotate(func(a1 *a, a2 *a, a3 *a) *b {\n\t\t\t\treturn &b{a2}\n\t\t\t}, fx.ParamTags(`name:\"firstA\"`, `name:\"secondA\"`, `name:\"thirdA\"`))))\n\n\t\trequire.NoError(t, app.Err())\n\t\tdefer app.RequireStart().RequireStop()\n\t})\n\n\tt.Run(\"provide an already provided function using Annotate\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tapp := NewForTest(t,\n\t\t\tfx.Provide(fx.Annotate(newA, fx.ResultTags(`name:\"a\"`))),\n\t\t\tfx.Provide(fx.Annotate(newA, fx.ResultTags(`name:\"a\"`))),\n\t\t\tfx.Invoke(\n\t\t\t\tfx.Annotate(newB, fx.ParamTags(`name:\"a\"`)),\n\t\t\t),\n\t\t)\n\t\terr := app.Err()\n\t\trequire.Error(t, err)\n\t\tassert.Contains(t, err.Error(), \"already provided\")\n\t\tassert.Contains(t, err.Error(), \"go.uber.org/fx_test.TestAnnotate.func\")\n\t})\n\n\tt.Run(\"specify more ParamTags than Params\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tapp := fxtest.New(t,\n\t\t\tfx.Provide(\n\t\t\t\t// This should just leave newA as it is.\n\t\t\t\tfx.Annotate(newA, fx.ParamTags(`name:\"something\"`)),\n\t\t\t),\n\t\t\tfx.Invoke(newB),\n\t\t)\n\n\t\terr := app.Err()\n\t\trequire.NoError(t, err)\n\t\tdefer app.RequireStart().RequireStop()\n\t})\n\n\tt.Run(\"specify two ParamTags\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tapp := NewForTest(t,\n\t\t\tfx.Provide(\n\t\t\t\t// This should just leave newA as it is.\n\t\t\t\tfx.Annotate(\n\t\t\t\t\tnewA,\n\t\t\t\t\tfx.ParamTags(`name:\"something\"`),\n\t\t\t\t\tfx.ParamTags(`name:\"anotherThing\"`),\n\t\t\t\t),\n\t\t\t),\n\t\t\tfx.Invoke(newB),\n\t\t)\n\t\terr := app.Err()\n\t\trequire.Error(t, err)\n\t\tassert.Contains(t, err.Error(), \"encountered error while applying annotation using fx.Annotate to go.uber.org/fx_test.TestAnnotate.func1(): cannot apply more than one line of ParamTags\")\n\t})\n\n\tt.Run(\"specify two ResultTags\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tapp := NewForTest(t,\n\t\t\tfx.Provide(\n\t\t\t\t// This should just leave newA as it is.\n\t\t\t\tfx.Annotate(\n\t\t\t\t\tnewA,\n\t\t\t\t\tfx.ResultTags(`name:\"A\"`),\n\t\t\t\t\tfx.ResultTags(`name:\"AA\"`),\n\t\t\t\t),\n\t\t\t),\n\t\t\tfx.Invoke(newB),\n\t\t)\n\n\t\terr := app.Err()\n\t\trequire.Error(t, err)\n\t\tassert.Contains(t, err.Error(), \"encountered error while applying annotation using fx.Annotate to go.uber.org/fx_test.TestAnnotate.func1(): cannot apply more than one line of ResultTags\")\n\t})\n\n\tt.Run(\"annotate with a non-nil error\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tapp := NewForTest(t,\n\t\t\tfx.Provide(\n\t\t\t\tfx.Annotate(func() (*bytes.Buffer, error) {\n\t\t\t\t\tbuf := make([]byte, 1)\n\t\t\t\t\treturn bytes.NewBuffer(buf), errors.New(\"some error\")\n\t\t\t\t}, fx.ResultTags(`name:\"buf\"`))),\n\t\t\tfx.Invoke(\n\t\t\t\tfx.Annotate(func(b *bytes.Buffer) {\n\t\t\t\t\tb.Write([]byte{1})\n\t\t\t\t}, fx.ParamTags(`name:\"buf\"`))),\n\t\t)\n\t\terr := app.Err()\n\t\trequire.Error(t, err)\n\t\tassert.Contains(t, err.Error(), \"some error\")\n\t})\n\n\tt.Run(\"annotate with a non-nil error and nil error\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tapp := NewForTest(t,\n\t\t\tfx.Provide(\n\t\t\t\tfx.Annotate(func() (*bytes.Buffer, error) {\n\t\t\t\t\tbuf := make([]byte, 1)\n\t\t\t\t\treturn bytes.NewBuffer(buf), errors.New(\"some error\")\n\t\t\t\t}, fx.ResultTags(`name:\"buf1\"`)),\n\t\t\t\tfx.Annotate(func() (*bytes.Buffer, error) {\n\t\t\t\t\tbuf := make([]byte, 1)\n\t\t\t\t\treturn bytes.NewBuffer(buf), nil\n\t\t\t\t}, fx.ResultTags(`name:\"buf2\"`))),\n\t\t\tfx.Invoke(\n\t\t\t\tfx.Annotate(func(b1 *bytes.Buffer, b2 *bytes.Buffer) {\n\t\t\t\t\tb1.Write([]byte{1})\n\t\t\t\t\tb2.Write([]byte{1})\n\t\t\t\t}, fx.ParamTags(`name:\"buf1\"`, `name:\"buf2\"`))),\n\t\t)\n\t\terr := app.Err()\n\t\trequire.Error(t, err)\n\t\tassert.Contains(t, err.Error(), \"some error\")\n\t})\n\n\tt.Run(\"provide annotated non-function\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tapp := NewForTest(t,\n\t\t\tfx.Provide(\n\t\t\t\tfx.Annotate(42, fx.ResultTags(`name:\"buf\"`)),\n\t\t\t),\n\t\t)\n\t\terr := app.Err()\n\t\trequire.Error(t, err)\n\n\t\t// Example:\n\t\t// fx.Provide(fx.Annotate(42, fx.ResultTags([\"name:\\\"buf\\\"\"])) from:\n\t\t// go.uber.org/fx_test.TestAnnotate.func17\n\t\t//     /Users/abg/dev/fx/annotated_test.go:697\n\t\t// testing.tRunner\n\t\t//     /usr/local/Cellar/go/1.17.2/libexec/src/testing/testing.go:1259\n\t\t// Failed: must provide constructor function, got 42 (int)\n\n\t\tassert.Contains(t, err.Error(), \"fx.Provide(fx.Annotate(42\")\n\t\tassert.Contains(t, err.Error(), \"must provide constructor function, got 42 (int)\")\n\t})\n\n\tt.Run(\"invoke annotated non-function\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tapp := NewForTest(t,\n\t\t\tfx.Invoke(\n\t\t\t\tfx.Annotate(42, fx.ParamTags(`name:\"buf\"`)),\n\t\t\t),\n\t\t)\n\t\terr := app.Err()\n\t\trequire.Error(t, err)\n\t\tassert.Contains(t, err.Error(), \"must provide constructor function, got 42 (int)\")\n\t})\n\n\tt.Run(\"annotate a fx.Out with ResultTags\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\ttype A struct {\n\t\t\ts string\n\n\t\t\tfx.Out\n\t\t}\n\n\t\tf := func() A {\n\t\t\treturn A{s: \"hi\"}\n\t\t}\n\n\t\tapp := NewForTest(t,\n\t\t\tfx.Provide(\n\t\t\t\tfx.Annotate(f, fx.ResultTags(`name:\"out\"`)),\n\t\t\t),\n\t\t)\n\n\t\terr := app.Err()\n\t\trequire.Error(t, err)\n\t\tassert.Contains(t, err.Error(), \"fx.Out structs cannot be annotated with fx.ResultTags or fx.As\")\n\t})\n\n\tt.Run(\"annotate a fx.Out with As\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\ttype I any\n\n\t\ttype B struct {\n\t\t\t// implements I\n\t\t}\n\n\t\ttype Res struct {\n\t\t\tfx.Out\n\n\t\t\tAB B\n\t\t}\n\n\t\tf := func() Res {\n\t\t\treturn Res{AB: B{}}\n\t\t}\n\n\t\tapp := NewForTest(t,\n\t\t\tfx.Provide(\n\t\t\t\tfx.Annotate(f, fx.As(new(I))),\n\t\t\t),\n\t\t)\n\n\t\terr := app.Err()\n\t\trequire.Error(t, err)\n\t\tassert.Contains(t, err.Error(), \"fx.Out structs cannot be annotated with fx.ResultTags or fx.As\")\n\t})\n\n\tt.Run(\"annotate a fx.In with ParamTags\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\ttype A struct {\n\t\t\tS string\n\t\t}\n\t\ttype B struct {\n\t\t\tfx.In\n\t\t}\n\n\t\tapp := NewForTest(t,\n\t\t\tfx.Provide(\n\t\t\t\tfx.Annotate(func(i A) string { return i.S }, fx.ParamTags(`optional:\"true\"`)),\n\t\t\t\tfx.Annotate(func(i B) string { return \"ok\" }, fx.ParamTags(`name:\"problem\"`)),\n\t\t\t),\n\t\t)\n\t\terr := app.Err()\n\t\trequire.Error(t, err)\n\t\tassert.NotContains(t, err.Error(), \"invalid annotation function func(fx_test.A) string\")\n\t\tassert.Contains(t, err.Error(), \"invalid annotation function func(fx_test.B) string\")\n\t\tassert.Contains(t, err.Error(), \"fx.In structs cannot be annotated with fx.ParamTags or fx.From\")\n\t})\n\n\tt.Run(\"annotate a fx.In with From\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\ttype I any\n\n\t\ttype B struct {\n\t\t\t// implements I\n\t\t}\n\n\t\ttype Param struct {\n\t\t\tfx.In\n\t\t\tBInterface I\n\t\t}\n\n\t\tapp := NewForTest(t,\n\t\t\tfx.Provide(\n\t\t\t\tfx.Annotate(func(p Param) string { return \"ok\" }, fx.From(new(B))),\n\t\t\t),\n\t\t)\n\t\terr := app.Err()\n\t\trequire.Error(t, err)\n\t\tassert.Contains(t, err.Error(), \"invalid annotation function func(fx_test.Param) string\")\n\t\tassert.Contains(t, err.Error(), \"fx.In structs cannot be annotated with fx.ParamTags or fx.From\")\n\t})\n\n\tt.Run(\"annotate fx.In with fx.ResultTags\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\ttype A struct {\n\t\t\tfx.In\n\n\t\t\tI int\n\t\t}\n\n\t\tapp := NewForTest(t,\n\t\t\tfx.Provide(\n\t\t\t\tfx.Annotate(func(a A) string { return \"ok\" + strconv.Itoa(a.I) }, fx.ResultTags(`name:\"val\"`)),\n\t\t\t\tfunc() int {\n\t\t\t\t\treturn 1\n\t\t\t\t},\n\t\t\t),\n\t\t\tfx.Invoke(\n\t\t\t\tfx.Annotate(func(s string) {\n\t\t\t\t\tassert.Equal(t, \"ok1\", s)\n\t\t\t\t}, fx.ParamTags(`name:\"val\"`)),\n\t\t\t),\n\t\t)\n\t\terr := app.Err()\n\t\trequire.NoError(t, err)\n\t})\n\n\tt.Run(\"annotate fx.Out with fx.ParamTags\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\ttype A struct {\n\t\t\tfx.Out\n\n\t\t\tS string\n\t\t}\n\n\t\tapp := NewForTest(t,\n\t\t\tfx.Provide(\n\t\t\t\tfx.Annotate(func() int { return 1 }, fx.ResultTags(`name:\"val\"`)),\n\t\t\t\tfx.Annotate(func(i int) A { return A{S: strconv.Itoa(i)} }, fx.ParamTags(`name:\"val\"`)),\n\t\t\t),\n\t\t\tfx.Invoke(func(s string) {\n\t\t\t\tassert.Equal(t, \"1\", s)\n\t\t\t}),\n\t\t)\n\t\terr := app.Err()\n\t\trequire.NoError(t, err)\n\t})\n}\n\nfunc TestAnnotateApplyFail(t *testing.T) {\n\ttype a struct{}\n\ttype b struct{ a *a }\n\tnewA := func() *a { return &a{} }\n\tnewB := func(a *a) *b {\n\t\treturn &b{a}\n\t}\n\n\tvar (\n\t\terrTagSyntaxSpace            = `multiple tags are not separated by space`\n\t\terrTagKeySyntax              = \"tag key is invalid, Use group, name or optional as tag keys\"\n\t\terrTagValueSyntaxQuote       = `tag value should start with double quote. i.e. key:\"value\" `\n\t\terrTagValueSyntaxEndingQuote = `tag value should end in double quote. i.e. key:\"value\" `\n\t)\n\ttests := []struct {\n\t\tgive                 string\n\t\twantErr              string\n\t\tgiveAnnotationParam  fx.Annotation\n\t\tgiveAnnotationResult fx.Annotation\n\t}{\n\t\t{\n\t\t\tgive:                 \"Tags value invalid ending quote\",\n\t\t\twantErr:              errTagValueSyntaxEndingQuote,\n\t\t\tgiveAnnotationParam:  fx.ParamTags(`name:\"something'`),\n\t\t\tgiveAnnotationResult: fx.ResultTags(`name:\"something'`),\n\t\t},\n\t\t{\n\t\t\tgive:                 \"Tags value wrong starting quote\",\n\t\t\twantErr:              errTagValueSyntaxQuote,\n\t\t\tgiveAnnotationParam:  fx.ParamTags(`name:\"something\" optional:'true\"`),\n\t\t\tgiveAnnotationResult: fx.ResultTags(`name:\"something\" optional:'true\"`),\n\t\t},\n\t\t{\n\t\t\tgive:                 \"Tags multiple tags not separated by space\",\n\t\t\twantErr:              errTagSyntaxSpace,\n\t\t\tgiveAnnotationParam:  fx.ParamTags(`name:\"something\"group:\"something\"`),\n\t\t\tgiveAnnotationResult: fx.ResultTags(`name:\"something\"group:\"something\"`),\n\t\t},\n\t\t{\n\t\t\tgive:                 \"Tags key not equal to group, name or optional\",\n\t\t\twantErr:              errTagKeySyntax,\n\t\t\tgiveAnnotationParam:  fx.ParamTags(`name1:\"something\"`),\n\t\t\tgiveAnnotationResult: fx.ResultTags(`name1:\"something\"`),\n\t\t},\n\t\t{\n\t\t\tgive:                 \"Tags key empty\",\n\t\t\twantErr:              errTagKeySyntax,\n\t\t\tgiveAnnotationParam:  fx.ParamTags(`:\"something\"`),\n\t\t\tgiveAnnotationResult: fx.ResultTags(`:\"something\"`),\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(\"Param \"+tt.give, func(t *testing.T) {\n\t\t\tapp := NewForTest(t,\n\t\t\t\tfx.Provide(\n\t\t\t\t\tfx.Annotate(\n\t\t\t\t\t\tnewA,\n\t\t\t\t\t\ttt.giveAnnotationParam,\n\t\t\t\t\t),\n\t\t\t\t),\n\t\t\t\tfx.Invoke(newB),\n\t\t\t)\n\t\t\tassert.ErrorContains(t, app.Err(), tt.wantErr)\n\t\t})\n\t\tt.Run(\"Result \"+tt.give, func(t *testing.T) {\n\t\t\tapp := NewForTest(t,\n\t\t\t\tfx.Provide(\n\t\t\t\t\tfx.Annotate(\n\t\t\t\t\t\tnewA,\n\t\t\t\t\t\ttt.giveAnnotationResult,\n\t\t\t\t\t),\n\t\t\t\t),\n\t\t\t\tfx.Invoke(newB),\n\t\t\t)\n\t\t\tassert.ErrorContains(t, app.Err(), tt.wantErr)\n\t\t})\n\t}\n}\n\nfunc TestAnnotateApplySuccess(t *testing.T) {\n\ttype a struct{}\n\ttype b struct{ a *a }\n\tnewA := func() *a { return &a{} }\n\tnewB := func(a *a) *b {\n\t\treturn &b{a}\n\t}\n\n\ttests := []struct {\n\t\tgive                 string\n\t\tgiveAnnotationParam  fx.Annotation\n\t\tgiveAnnotationResult fx.Annotation\n\t}{\n\t\t{\n\t\t\tgive:                 \"ParamTags Tag Empty\",\n\t\t\tgiveAnnotationParam:  fx.ParamTags(`  `),\n\t\t\tgiveAnnotationResult: fx.ResultTags(`  `),\n\t\t},\n\t\t{\n\t\t\tgive:                 \"ParamTags Tag Empty with extra spaces\",\n\t\t\tgiveAnnotationParam:  fx.ParamTags(`name:\"versionNum\"`, `  `),\n\t\t\tgiveAnnotationResult: fx.ResultTags(`   `, `group:\"versionNum\"`),\n\t\t},\n\t\t{\n\t\t\tgive:                 \"ParamTags Tag with \\\\ \",\n\t\t\tgiveAnnotationParam:  fx.ParamTags(`name:\"version\\\\Num\"`, `  `),\n\t\t\tgiveAnnotationResult: fx.ResultTags(``, `group:\"version\\\\Num\"`),\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.give, func(t *testing.T) {\n\t\t\tapp := NewForTest(t,\n\t\t\t\tfx.Provide(\n\t\t\t\t\tfx.Annotate(\n\t\t\t\t\t\tnewA,\n\t\t\t\t\t\ttt.giveAnnotationParam,\n\t\t\t\t\t\ttt.giveAnnotationResult,\n\t\t\t\t\t),\n\t\t\t\t),\n\t\t\t\tfx.Invoke(newB),\n\t\t\t)\n\t\t\trequire.NoError(t, app.Err())\n\t\t})\n\t}\n}\n\nfunc assertApp(\n\tt *testing.T,\n\tapp interface {\n\t\tStart(context.Context) error\n\t\tStop(context.Context) error\n\t},\n\tstarted *bool,\n\tstopped *bool,\n\tinvoked *bool,\n) {\n\tt.Helper()\n\tctx, cancel := context.WithCancel(context.Background())\n\tdefer cancel()\n\tassert.False(t, *started)\n\trequire.NoError(t, app.Start(ctx))\n\tassert.True(t, *started)\n\n\tif invoked != nil {\n\t\tassert.True(t, *invoked)\n\t}\n\n\tif stopped != nil {\n\t\tassert.False(t, *stopped)\n\t\trequire.NoError(t, app.Stop(ctx))\n\t\tassert.True(t, *stopped)\n\t}\n\n\tdefer app.Stop(ctx)\n}\n\nfunc TestHookAnnotations(t *testing.T) {\n\tt.Parallel()\n\n\ttype a struct{}\n\ttype b struct{ a *a }\n\ttype c struct{ b *b }\n\tnewB := func(a *a) *b {\n\t\treturn &b{a}\n\t}\n\tnewC := func(b *b) *c {\n\t\treturn &c{b}\n\t}\n\n\tt.Run(\"with hook on invoke\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tvar (\n\t\t\tstarted bool\n\t\t\tstopped bool\n\t\t\tinvoked bool\n\t\t)\n\t\thook := fx.Annotate(\n\t\t\tfunc() {\n\t\t\t\tinvoked = true\n\t\t\t},\n\t\t\tfx.OnStart(func(context.Context) error {\n\t\t\t\tstarted = true\n\t\t\t\treturn nil\n\t\t\t}),\n\t\t\tfx.OnStop(func(context.Context) error {\n\t\t\t\tstopped = true\n\t\t\t\treturn nil\n\t\t\t}),\n\t\t)\n\t\tapp := fxtest.New(t, fx.Invoke(hook))\n\n\t\tassertApp(t, app, &started, &stopped, &invoked)\n\t})\n\n\tt.Run(\"depend on result interface of target\", func(t *testing.T) {\n\t\ttype stub interface {\n\t\t\tString() string\n\t\t}\n\n\t\tvar started bool\n\n\t\thook := fx.Annotate(\n\t\t\tfunc() (stub, error) {\n\t\t\t\tb := []byte(\"expected\")\n\t\t\t\treturn bytes.NewBuffer(b), nil\n\t\t\t},\n\t\t\tfx.OnStart(func(_ context.Context, s stub) error {\n\t\t\t\tstarted = true\n\t\t\t\trequire.Equal(t, \"expected\", s.String())\n\t\t\t\treturn nil\n\t\t\t}),\n\t\t)\n\n\t\tapp := fxtest.New(t,\n\t\t\tfx.Provide(hook),\n\t\t\tfx.Invoke(func(s stub) {\n\t\t\t\trequire.Equal(t, \"expected\", s.String())\n\t\t\t}),\n\t\t)\n\n\t\tassertApp(t, app, &started, nil, nil)\n\t})\n\n\tt.Run(\"start and stop without dependencies\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\ttype stub any\n\n\t\tvar (\n\t\t\tinvoked bool\n\t\t\tstarted bool\n\t\t\tstopped bool\n\t\t)\n\n\t\thook := fx.Annotate(\n\t\t\tfunc() (stub, error) { return nil, nil },\n\t\t\tfx.OnStart(func(context.Context) error {\n\t\t\t\tstarted = true\n\t\t\t\treturn nil\n\t\t\t}),\n\t\t\tfx.OnStop(func(context.Context) error {\n\t\t\t\tstopped = true\n\t\t\t\treturn nil\n\t\t\t}),\n\t\t)\n\n\t\tapp := fxtest.New(t,\n\t\t\tfx.Provide(hook),\n\t\t\tfx.Invoke(func(s stub) {\n\t\t\t\tinvoked = s == nil\n\t\t\t}),\n\t\t)\n\n\t\tassertApp(t, app, &started, &stopped, &invoked)\n\t})\n\n\tt.Run(\"with multiple extra dependency parameters\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\ttype (\n\t\t\tA any\n\t\t\tB any\n\t\t\tC any\n\t\t)\n\n\t\tvar value int\n\n\t\thook := fx.Annotate(\n\t\t\tfunc(b B, c C) (A, error) { return nil, nil },\n\t\t\tfx.OnStart(func(_ context.Context, b B, c C) error {\n\t\t\t\tb1, _ := b.(int)\n\t\t\t\tc1, _ := c.(int)\n\t\t\t\tvalue = b1 + c1\n\t\t\t\treturn nil\n\t\t\t}),\n\t\t)\n\n\t\tapp := fxtest.New(t,\n\t\t\tfx.Provide(hook),\n\t\t\tfx.Provide(func() B { return int(1) }),\n\t\t\tfx.Provide(func() C { return int(2) }),\n\t\t\tfx.Invoke(func(A) {}),\n\t\t)\n\n\t\tctx := context.Background()\n\t\tassert.Zero(t, value)\n\t\trequire.NoError(t, app.Start(ctx))\n\t\tdefer func() {\n\t\t\trequire.NoError(t, app.Stop(ctx))\n\t\t}()\n\t\tassert.Equal(t, 3, value)\n\t})\n\n\tt.Run(\"with Supply\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\ttype A interface {\n\t\t\tWriteString(string) (int, error)\n\t\t}\n\n\t\tbuf := bytes.NewBuffer(nil)\n\t\tvar called bool\n\n\t\tctor := fx.Provide(\n\t\t\tfx.Annotate(\n\t\t\t\tfunc(s fmt.Stringer) A {\n\t\t\t\t\treturn buf\n\t\t\t\t},\n\t\t\t\tfx.OnStart(func(_ context.Context, a A, s fmt.Stringer) error {\n\t\t\t\t\ta.WriteString(s.String())\n\t\t\t\t\treturn nil\n\t\t\t\t}),\n\t\t\t),\n\t\t)\n\n\t\tsupply := fx.Supply(\n\t\t\tfx.Annotate(\n\t\t\t\t&asStringer{\"supply\"},\n\t\t\t\tfx.OnStart(func(context.Context) error {\n\t\t\t\t\tcalled = true\n\t\t\t\t\treturn nil\n\t\t\t\t}),\n\t\t\t\tfx.As(new(fmt.Stringer)),\n\t\t\t))\n\n\t\topts := fx.Options(\n\t\t\tctor,\n\t\t\tsupply,\n\t\t\tfx.Invoke(func(A) {}),\n\t\t)\n\n\t\tapp := fxtest.New(t, opts)\n\t\tctx := context.Background()\n\t\trequire.False(t, called)\n\t\terr := app.Start(ctx)\n\t\trequire.NoError(t, err)\n\t\trequire.NoError(t, app.Stop(ctx))\n\t\trequire.Equal(t, \"supply\", buf.String())\n\t\trequire.True(t, called)\n\t})\n\n\tt.Run(\"with Decorate\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\ttype A interface {\n\t\t\tWriteString(string) (int, error)\n\t\t}\n\n\t\tbuf := bytes.NewBuffer(nil)\n\t\tctor := fx.Provide(func() A { return buf })\n\n\t\tvar called bool\n\n\t\thook := fx.Annotate(\n\t\t\tfunc(in A) A {\n\t\t\t\tin.WriteString(\"decorated\")\n\t\t\t\treturn in\n\t\t\t},\n\t\t\tfx.OnStart(func(_ context.Context, _ A) error {\n\t\t\t\tcalled = assert.Equal(t, \"decorated\", buf.String())\n\t\t\t\treturn nil\n\t\t\t}),\n\t\t)\n\n\t\tdecorated := fx.Decorate(hook)\n\n\t\topts := fx.Options(\n\t\t\tctor,\n\t\t\tdecorated,\n\t\t\tfx.Invoke(func(A) {}),\n\t\t)\n\n\t\tapp := fxtest.New(t, opts)\n\t\tctx := context.Background()\n\t\trequire.NoError(t, app.Start(ctx))\n\t\trequire.NoError(t, app.Stop(ctx))\n\t\trequire.True(t, called)\n\t\trequire.Equal(t, \"decorated\", buf.String())\n\t})\n\n\tt.Run(\"with Decorate and tags\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\ttype A interface {\n\t\t\tWriteString(string) (int, error)\n\t\t}\n\n\t\tbuf := bytes.NewBuffer(nil)\n\t\tctor := fx.Provide(\n\t\t\tfx.Annotate(\n\t\t\t\tfunc() A { return buf },\n\t\t\t\tfx.ResultTags(`name:\"name\"`),\n\t\t\t),\n\t\t)\n\n\t\tvar called bool\n\n\t\ttype hookParam struct {\n\t\t\tfx.In\n\t\t\tA A `name:\"name\"`\n\t\t}\n\n\t\thook := fx.Annotate(\n\t\t\tfunc(in A) A {\n\t\t\t\tin.WriteString(\"decorated\")\n\t\t\t\treturn in\n\t\t\t},\n\t\t\tfx.ParamTags(`name:\"name\"`),\n\t\t\tfx.ResultTags(`name:\"name\"`),\n\t\t\tfx.OnStart(func(_ context.Context, _ hookParam) error {\n\t\t\t\tcalled = assert.Equal(t, \"decorated\", buf.String())\n\t\t\t\treturn nil\n\t\t\t}),\n\t\t)\n\n\t\tdecorated := fx.Decorate(hook)\n\n\t\topts := fx.Options(\n\t\t\tctor,\n\t\t\tdecorated,\n\t\t\tfx.Invoke(fx.Annotate(func(A) {}, fx.ParamTags(`name:\"name\"`))),\n\t\t)\n\n\t\tapp := fxtest.New(t, opts)\n\t\tctx := context.Background()\n\t\trequire.NoError(t, app.Start(ctx))\n\t\trequire.NoError(t, app.Stop(ctx))\n\t\trequire.True(t, called)\n\t\trequire.Equal(t, \"decorated\", buf.String())\n\t})\n\n\tt.Run(\"with Supply and Decorate\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\ttype A any\n\n\t\tch := make(chan string, 3)\n\n\t\thook := fx.Annotate(\n\t\t\tfunc(s fmt.Stringer) A { return nil },\n\t\t\tfx.OnStart(func(_ context.Context, s fmt.Stringer) error {\n\t\t\t\tch <- \"constructor\"\n\t\t\t\trequire.Equal(t, \"supply\", s.String())\n\t\t\t\treturn nil\n\t\t\t}),\n\t\t)\n\n\t\tctor := fx.Provide(hook)\n\n\t\thook = fx.Annotate(\n\t\t\t&asStringer{\"supply\"},\n\t\t\tfx.OnStart(func(_ context.Context) error {\n\t\t\t\tch <- \"supply\"\n\t\t\t\treturn nil\n\t\t\t}),\n\t\t\tfx.As(new(fmt.Stringer)),\n\t\t)\n\n\t\tsupply := fx.Supply(hook)\n\n\t\thook = fx.Annotate(\n\t\t\tfunc(in A) A { return in },\n\t\t\tfx.OnStart(func(_ context.Context) error {\n\t\t\t\tch <- \"decorated\"\n\t\t\t\treturn nil\n\t\t\t}),\n\t\t)\n\n\t\tdecorated := fx.Decorate(hook)\n\n\t\topts := fx.Options(\n\t\t\tctor,\n\t\t\tsupply,\n\t\t\tdecorated,\n\t\t\tfx.Invoke(func(A) {}),\n\t\t)\n\n\t\tapp := fxtest.New(t, opts)\n\t\tctx := context.Background()\n\t\terr := app.Start(ctx)\n\t\trequire.NoError(t, err)\n\t\trequire.NoError(t, app.Stop(ctx))\n\t\tclose(ch)\n\n\t\trequire.Equal(t, \"supply\", <-ch)\n\t\trequire.Equal(t, \"constructor\", <-ch)\n\t\trequire.Equal(t, \"decorated\", <-ch)\n\t})\n\n\tt.Run(\"Annotated params work with lifecycle hook annotations\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\ttype paramStruct struct {\n\t\t\tfx.In\n\t\t\tA *a `name:\"a\" optional:\"true\"`\n\t\t\tB *b `name:\"b\"`\n\t\t}\n\n\t\tapp := fxtest.New(t,\n\t\t\tfx.Provide(\n\t\t\t\tfx.Annotate(newB, fx.ParamTags(`optional:\"true\"`)),\n\t\t\t\tfx.Annotate(func(a *a, b *b) any { return nil },\n\t\t\t\t\tfx.ParamTags(`name:\"a\" optional:\"true\"`, `name:\"b\"`),\n\t\t\t\t\tfx.ResultTags(`name:\"nil\"`),\n\t\t\t\t\tfx.OnStart(func(_ paramStruct) error {\n\t\t\t\t\t\treturn nil\n\t\t\t\t\t}),\n\t\t\t\t\tfx.OnStop(func(_ paramStruct) error {\n\t\t\t\t\t\treturn nil\n\t\t\t\t\t}),\n\t\t\t\t),\n\t\t\t),\n\t\t\tfx.Invoke(newC),\n\t\t)\n\t\tdefer app.RequireStart().RequireStop()\n\t\trequire.NoError(t, app.Err())\n\t})\n\n\tt.Run(\"provide with annotated results and lifecycle hook appended\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\ttype firstAHookParam struct {\n\t\t\tfx.In\n\t\t\tCtx context.Context\n\t\t\tA   *a `name:\"firstA\"`\n\t\t}\n\t\ttype secondAHookParam struct {\n\t\t\tfx.In\n\t\t\tA   *a `name:\"secondA\"`\n\t\t\tCtx context.Context\n\t\t}\n\t\ttype thirdAHookParam struct {\n\t\t\tfx.In\n\t\t\tCtx context.Context\n\t\t\tA   *a `name:\"thirdA\"`\n\t\t}\n\n\t\tapp := fxtest.New(t,\n\t\t\tfx.Provide(\n\t\t\t\tfx.Annotate(func() *a {\n\t\t\t\t\treturn &a{}\n\t\t\t\t}, fx.ResultTags(`name:\"firstA\"`),\n\t\t\t\t\tfx.OnStart(func(param firstAHookParam) error {\n\t\t\t\t\t\trequire.NotNil(t, param.Ctx, \"context should be given\")\n\t\t\t\t\t\treturn nil\n\t\t\t\t\t})),\n\t\t\t\tfx.Annotate(func() *a {\n\t\t\t\t\treturn &a{}\n\t\t\t\t}, fx.ResultTags(`name:\"secondA\"`),\n\t\t\t\t\tfx.OnStart(func(param secondAHookParam) error {\n\t\t\t\t\t\trequire.NotNil(t, param.Ctx, \"context not correctly injected\")\n\t\t\t\t\t\treturn nil\n\t\t\t\t\t})),\n\t\t\t\tfx.Annotate(func() *a {\n\t\t\t\t\treturn &a{}\n\t\t\t\t}, fx.ResultTags(`name:\"thirdA\"`),\n\t\t\t\t\tfx.OnStart(func(param thirdAHookParam) error {\n\t\t\t\t\t\trequire.NotNil(t, param.Ctx, \"context not correctly injected\")\n\t\t\t\t\t\treturn nil\n\t\t\t\t\t})),\n\t\t\t\tfx.Annotate(func(a1 *a, a2 *a, a3 *a) *b {\n\t\t\t\t\treturn &b{a1}\n\t\t\t\t}, fx.ParamTags(\n\t\t\t\t\t`name:\"firstA\"`,\n\t\t\t\t\t`name:\"secondA\"`,\n\t\t\t\t\t`name:\"thirdA\"`,\n\t\t\t\t)),\n\t\t\t),\n\t\t\tfx.Invoke(newC),\n\t\t)\n\n\t\trequire.NoError(t, app.Err())\n\t\tdefer app.RequireStart().RequireStop()\n\t})\n\n\tt.Run(\"provide with optional params and lifecycle hook\", func(t *testing.T) {\n\t\ttype taggedHookParam struct {\n\t\t\tfx.In\n\t\t\tCtx context.Context\n\t\t\tA   *a `optional:\"true\"`\n\t\t}\n\t\tt.Parallel()\n\t\tapp := fxtest.New(t,\n\t\t\tfx.Provide(\n\t\t\t\tfx.Annotate(\n\t\t\t\t\tnewB,\n\t\t\t\t\tfx.ParamTags(`optional:\"true\"`),\n\t\t\t\t\tfx.OnStart(func(tp taggedHookParam, B *b) {\n\t\t\t\t\t\tfmt.Println(tp.A)\n\t\t\t\t\t\trequire.NotNil(t, tp.Ctx, \"context not correctly injected\")\n\t\t\t\t\t}),\n\t\t\t\t),\n\t\t\t),\n\t\t\tfx.Invoke(newC),\n\t\t)\n\n\t\trequire.NoError(t, app.Err())\n\t\tdefer app.RequireStart().RequireStop()\n\t})\n}\n\nfunc TestHookAnnotationFailures(t *testing.T) {\n\tt.Parallel()\n\tvalidateApp := func(t *testing.T, opts ...fx.Option) error {\n\t\treturn fx.ValidateApp(\n\t\t\tappend(opts, fx.Logger(fxtest.NewTestPrinter(t)))...,\n\t\t)\n\t}\n\n\ttype (\n\t\tA any\n\t\tB any\n\t)\n\n\ttype namedAndGroupHookParams struct {\n\t\tfx.In\n\t\tCtx context.Context\n\t\tA   *A `name:\"a\" group:\"groupA\"`\n\t}\n\n\ttype namedHookParam struct {\n\t\tfx.In\n\t\tCtx context.Context\n\t\tA   *A `name:\"a\"`\n\t}\n\n\ttype groupedHookParam struct {\n\t\tfx.In\n\t\tCtx context.Context\n\t\tA   *A `group:\"groupA\"`\n\t}\n\n\ttable := []struct {\n\t\tname        string\n\t\tannotation  any\n\t\textraOpts   fx.Option\n\t\tuseNew      bool\n\t\terrContains string\n\t}{\n\t\t{\n\t\t\tname:        \"with unprovided dependency\",\n\t\t\terrContains: \"hook function takes in a parameter of\",\n\t\t\tuseNew:      true,\n\t\t\tannotation: fx.Annotate(\n\t\t\t\tfunc() A { return nil },\n\t\t\t\tfx.OnStart(func(context.Context, B) error {\n\t\t\t\t\treturn nil\n\t\t\t\t}),\n\t\t\t),\n\t\t},\n\t\t{\n\t\t\tname:        \"with hook that errors\",\n\t\t\terrContains: \"hook failed\",\n\t\t\tuseNew:      true,\n\t\t\tannotation: fx.Annotate(\n\t\t\t\tfunc() (A, error) { return nil, nil },\n\t\t\t\tfx.OnStart(func(context.Context) error {\n\t\t\t\t\treturn errors.New(\"hook failed\")\n\t\t\t\t}),\n\t\t\t),\n\t\t},\n\t\t{\n\t\t\tname:        \"with multiple hooks of the same type\",\n\t\t\terrContains: `cannot apply more than one \"OnStart\" hook annotation`,\n\t\t\tannotation: fx.Annotate(\n\t\t\t\tfunc() A { return nil },\n\t\t\t\tfx.OnStart(func(context.Context) error { return nil }),\n\t\t\t\tfx.OnStart(func(context.Context) error { return nil }),\n\t\t\t),\n\t\t},\n\t\t{\n\t\t\tname:        \"with constructor that errors\",\n\t\t\terrContains: \"hooks should not be installed\",\n\t\t\tuseNew:      true,\n\t\t\tannotation: fx.Annotate(\n\t\t\t\tfunc() (A, error) {\n\t\t\t\t\treturn nil, errors.New(\"hooks should not be installed\")\n\t\t\t\t},\n\t\t\t\tfx.OnStart(func(context.Context) error {\n\t\t\t\t\trequire.FailNow(t, \"hook should not be called\")\n\t\t\t\t\treturn nil\n\t\t\t\t}),\n\t\t\t),\n\t\t},\n\t\t{\n\t\t\tname:        \"without a function target\",\n\t\t\terrContains: \"must provide function\",\n\t\t\tannotation: fx.Annotate(\n\t\t\t\tfunc() A { return nil },\n\t\t\t\tfx.OnStart(&struct{}{}),\n\t\t\t),\n\t\t},\n\t\t{\n\t\t\tname:        \"invalid return: non-error return\",\n\t\t\terrContains: \"optional hook return may only be an error\",\n\t\t\tannotation: fx.Annotate(\n\t\t\t\tfunc() A { return nil },\n\t\t\t\tfx.OnStart(func(context.Context) any {\n\t\t\t\t\treturn nil\n\t\t\t\t}),\n\t\t\t),\n\t\t},\n\t\t{\n\t\t\tname:        \"invalid return: too many returns\",\n\t\t\terrContains: \"optional hook return may only be an error\",\n\t\t\tannotation: fx.Annotate(\n\t\t\t\tfunc() A { return nil },\n\t\t\t\tfx.OnStart(func(context.Context) (any, any) {\n\t\t\t\t\treturn nil, nil\n\t\t\t\t}),\n\t\t\t),\n\t\t},\n\t\t{\n\t\t\tname:        \"with variactic hook\",\n\t\t\terrContains: \"must not accept variadic\",\n\t\t\tannotation: fx.Annotate(\n\t\t\t\tfunc() A { return nil },\n\t\t\t\tfx.OnStart(func(context.Context, ...A) error {\n\t\t\t\t\treturn nil\n\t\t\t\t}),\n\t\t\t),\n\t\t},\n\t\t{\n\t\t\tname:        \"with nil hook target\",\n\t\t\terrContains: \"cannot use nil function\",\n\t\t\tannotation: fx.Annotate(\n\t\t\t\tfunc() A { return nil },\n\t\t\t\tfx.OnStop(nil),\n\t\t\t),\n\t\t},\n\t\t{\n\t\t\tname:        \"cannot pull in any extra dependency other than params or results of the annotated function\",\n\t\t\terrContains: \"hook function takes in a parameter of \\\"fx_test.B\\\"\",\n\t\t\tuseNew:      true,\n\t\t\tannotation: fx.Annotate(\n\t\t\t\tfunc(s string) A { return nil },\n\t\t\t\tfx.OnStart(func(b B) error { return nil }),\n\t\t\t),\n\t\t\textraOpts: fx.Options(\n\t\t\t\tfx.Provide(func() string { return \"test\" }),\n\t\t\t\tfx.Provide(func() B { return nil }),\n\t\t\t),\n\t\t},\n\t\t{\n\t\t\tname:        \"cannot pull in a dependency when it's not properly named\",\n\t\t\terrContains: \"hook function takes in a parameter of \\\"*fx_test.A `name:\\\"a\\\"`\\\"\",\n\t\t\tuseNew:      true,\n\t\t\tannotation: fx.Annotate(\n\t\t\t\tfunc(s A) A { return nil },\n\t\t\t\tfx.OnStart(func(b namedHookParam) error { return nil }),\n\t\t\t),\n\t\t},\n\t\t{\n\t\t\tname:        \"cannot pull in a dependency when it's not properly grouped\",\n\t\t\terrContains: \"hook function takes in a parameter of \\\"*fx_test.A `group:\\\"groupA\\\"`\",\n\t\t\tuseNew:      true,\n\t\t\tannotation: fx.Annotate(\n\t\t\t\tfunc(s A) A { return nil },\n\t\t\t\tfx.OnStart(func(b groupedHookParam) error { return nil }),\n\t\t\t),\n\t\t},\n\t\t{\n\t\t\tname:        \"cannot pull in a dependency when it's not properly named and grouped\",\n\t\t\terrContains: \"hook function takes in a parameter of \\\"*fx_test.A `name:\\\"a\\\" group:\\\"groupA\\\"`\",\n\t\t\tuseNew:      true,\n\t\t\tannotation: fx.Annotate(\n\t\t\t\tfunc(s A) A { return nil },\n\t\t\t\tfx.OnStart(func(b namedAndGroupHookParams) error { return nil }),\n\t\t\t),\n\t\t},\n\t}\n\n\tfor _, tt := range table {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tt.Parallel()\n\t\t\topts := fx.Options(\n\t\t\t\tfx.Provide(tt.annotation),\n\t\t\t\tfx.Invoke(func(A) {}),\n\t\t\t)\n\n\t\t\tif tt.extraOpts != nil {\n\t\t\t\topts = fx.Options(opts, tt.extraOpts)\n\t\t\t}\n\n\t\t\tif !tt.useNew {\n\t\t\t\terr := validateApp(t, opts)\n\t\t\t\trequire.Error(t, err)\n\t\t\t\trequire.Contains(t, err.Error(), tt.errContains)\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tapp := NewForTest(t, opts)\n\t\t\terr := app.Start(context.Background())\n\t\t\trequire.Error(t, err)\n\t\t\trequire.Contains(t, err.Error(), tt.errContains)\n\t\t})\n\t}\n}\n\nfunc TestHookAnnotationFunctionFlexibility(t *testing.T) {\n\ttype A any\n\n\ttable := []struct {\n\t\tname       string\n\t\tannotation any\n\t}{\n\t\t{\n\t\t\tname: \"without error return\",\n\t\t\tannotation: fx.Annotate(\n\t\t\t\tfunc(called *atomic.Bool) A { return nil },\n\t\t\t\tfx.OnStart(func(_ context.Context, called *atomic.Bool) {\n\t\t\t\t\tcalled.Store(true)\n\t\t\t\t}),\n\t\t\t),\n\t\t},\n\t\t{\n\t\t\tname: \"without context param\",\n\t\t\tannotation: fx.Annotate(\n\t\t\t\tfunc(called *atomic.Bool) A { return nil },\n\t\t\t\tfx.OnStart(func(called *atomic.Bool) error {\n\t\t\t\t\tcalled.Store(true)\n\t\t\t\t\treturn nil\n\t\t\t\t}),\n\t\t\t),\n\t\t},\n\t\t{\n\t\t\tname: \"without context param or error return\",\n\t\t\tannotation: fx.Annotate(\n\t\t\t\tfunc(called *atomic.Bool) A { return nil },\n\t\t\t\tfx.OnStart(func(called *atomic.Bool) {\n\t\t\t\t\tcalled.Store(true)\n\t\t\t\t}),\n\t\t\t),\n\t\t},\n\t\t{\n\t\t\tname: \"with context param and error return\",\n\t\t\tannotation: fx.Annotate(\n\t\t\t\tfunc(called *atomic.Bool) A { return nil },\n\t\t\t\tfx.OnStart(func(_ context.Context, called *atomic.Bool) error {\n\t\t\t\t\tcalled.Store(true)\n\t\t\t\t\treturn nil\n\t\t\t\t}),\n\t\t\t),\n\t\t},\n\t}\n\n\tfor _, tt := range table {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tvar (\n\t\t\t\tcalled atomic.Bool\n\t\t\t\topts   = fx.Options(\n\t\t\t\t\tfx.Provide(tt.annotation),\n\t\t\t\t\tfx.Supply(&called),\n\t\t\t\t\tfx.Invoke(func(A) {}),\n\t\t\t\t)\n\t\t\t)\n\n\t\t\tfxtest.New(t, opts).RequireStart().RequireStop()\n\t\t\trequire.True(t, called.Load())\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "app.go",
    "content": "// Copyright (c) 2020-2021 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage fx\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"os\"\n\t\"reflect\"\n\t\"strings\"\n\t\"time\"\n\n\t\"go.uber.org/dig\"\n\t\"go.uber.org/fx/fxevent\"\n\t\"go.uber.org/fx/internal/fxclock\"\n\t\"go.uber.org/fx/internal/fxlog\"\n\t\"go.uber.org/fx/internal/fxreflect\"\n\t\"go.uber.org/fx/internal/lifecycle\"\n\t\"go.uber.org/multierr\"\n)\n\n// DefaultTimeout is the default timeout for starting or stopping an\n// application. It can be configured with the [StartTimeout] and [StopTimeout]\n// options.\nconst DefaultTimeout = 15 * time.Second\n\n// An Option specifies the behavior of the application.\n// This is the primary means by which you interface with Fx.\n//\n// Zero or more options are specified at startup with [New].\n// Options cannot be changed once an application has been initialized.\n// Options may be grouped into a single option using the [Options] function.\n// A group of options providing a logical unit of functionality\n// may use [Module] to name that functionality\n// and scope certain operations to within that module.\ntype Option interface {\n\tfmt.Stringer\n\n\tapply(*module)\n}\n\n// Error registers any number of errors with the application to short-circuit\n// startup. If more than one error is given, the errors are combined into a\n// single error.\n//\n// Similar to invocations, errors are applied in order. All Provide and Invoke\n// options registered before or after an Error option will not be applied.\nfunc Error(errs ...error) Option {\n\treturn errorOption(errs)\n}\n\ntype errorOption []error\n\nfunc (errs errorOption) apply(mod *module) {\n\tmod.app.err = multierr.Append(mod.app.err, multierr.Combine(errs...))\n}\n\nfunc (errs errorOption) String() string {\n\treturn fmt.Sprintf(\"fx.Error(%v)\", multierr.Combine(errs...))\n}\n\n// Options bundles a group of options together into a single option.\n//\n// Use Options to group together options that don't belong in a [Module].\n//\n//\tvar loggingAndMetrics = fx.Options(\n//\t\tlogging.Module,\n//\t\tmetrics.Module,\n//\t\tfx.Invoke(func(logger *log.Logger) {\n//\t\t\tapp.globalLogger = logger\n//\t\t}),\n//\t)\nfunc Options(opts ...Option) Option {\n\treturn optionGroup(opts)\n}\n\ntype optionGroup []Option\n\nfunc (og optionGroup) apply(mod *module) {\n\tfor _, opt := range og {\n\t\topt.apply(mod)\n\t}\n}\n\nfunc (og optionGroup) String() string {\n\titems := make([]string, len(og))\n\tfor i, opt := range og {\n\t\titems[i] = fmt.Sprint(opt)\n\t}\n\treturn fmt.Sprintf(\"fx.Options(%s)\", strings.Join(items, \", \"))\n}\n\n// StartTimeout changes the application's start timeout.\n// This controls the total time that all [OnStart] hooks have to complete.\n// If the timeout is exceeded, the application will fail to start.\n//\n// Defaults to [DefaultTimeout].\nfunc StartTimeout(v time.Duration) Option {\n\treturn startTimeoutOption(v)\n}\n\ntype startTimeoutOption time.Duration\n\nfunc (t startTimeoutOption) apply(m *module) {\n\tif m.parent != nil {\n\t\tm.app.err = fmt.Errorf(\"fx.StartTimeout Option should be passed to top-level App, \" +\n\t\t\t\"not to fx.Module\")\n\t} else {\n\t\tm.app.startTimeout = time.Duration(t)\n\t}\n}\n\nfunc (t startTimeoutOption) String() string {\n\treturn fmt.Sprintf(\"fx.StartTimeout(%v)\", time.Duration(t))\n}\n\n// StopTimeout changes the application's stop timeout.\n// This controls the total time that all [OnStop] hooks have to complete.\n// If the timeout is exceeded, the application will exit early.\n//\n// Defaults to [DefaultTimeout].\nfunc StopTimeout(v time.Duration) Option {\n\treturn stopTimeoutOption(v)\n}\n\ntype stopTimeoutOption time.Duration\n\nfunc (t stopTimeoutOption) apply(m *module) {\n\tif m.parent != nil {\n\t\tm.app.err = fmt.Errorf(\"fx.StopTimeout Option should be passed to top-level App, \" +\n\t\t\t\"not to fx.Module\")\n\t} else {\n\t\tm.app.stopTimeout = time.Duration(t)\n\t}\n}\n\nfunc (t stopTimeoutOption) String() string {\n\treturn fmt.Sprintf(\"fx.StopTimeout(%v)\", time.Duration(t))\n}\n\n// RecoverFromPanics causes panics that occur in functions given to [Provide],\n// [Decorate], and [Invoke] to be recovered from.\n// This error can be retrieved as any other error, by using (*App).Err().\nfunc RecoverFromPanics() Option {\n\treturn recoverFromPanicsOption{}\n}\n\ntype recoverFromPanicsOption struct{}\n\nfunc (o recoverFromPanicsOption) apply(m *module) {\n\tif m.parent != nil {\n\t\tm.app.err = fmt.Errorf(\"fx.RecoverFromPanics Option should be passed to top-level \" +\n\t\t\t\"App, not to fx.Module\")\n\t} else {\n\t\tm.app.recoverFromPanics = true\n\t}\n}\n\nfunc (o recoverFromPanicsOption) String() string {\n\treturn \"fx.RecoverFromPanics()\"\n}\n\n// WithLogger specifies the [fxevent.Logger] used by Fx to log its own events\n// (e.g. a constructor was provided, a function was invoked, etc.).\n//\n// The argument to this is a constructor with one of the following return\n// types:\n//\n//\tfxevent.Logger\n//\t(fxevent.Logger, error)\n//\n// The constructor may depend on any other types provided to the application.\n// For example,\n//\n//\tWithLogger(func(logger *zap.Logger) fxevent.Logger {\n//\t  return &fxevent.ZapLogger{Logger: logger}\n//\t})\n//\n// If specified, Fx will construct the logger and log all its events to the\n// specified logger.\n//\n// If Fx fails to build the logger, or no logger is specified, it will fall back to\n// [fxevent.ConsoleLogger] configured to write to stderr.\nfunc WithLogger(constructor any) Option {\n\treturn withLoggerOption{\n\t\tconstructor: constructor,\n\t\tStack:       fxreflect.CallerStack(1, 0),\n\t}\n}\n\ntype withLoggerOption struct {\n\tconstructor any\n\tStack       fxreflect.Stack\n}\n\nfunc (l withLoggerOption) apply(m *module) {\n\tm.logConstructor = &provide{\n\t\tTarget: l.constructor,\n\t\tStack:  l.Stack,\n\t}\n}\n\nfunc (l withLoggerOption) String() string {\n\treturn fmt.Sprintf(\"fx.WithLogger(%s)\", fxreflect.FuncName(l.constructor))\n}\n\n// Printer is the interface required by Fx's logging backend. It's implemented\n// by most loggers, including the one bundled with the standard library.\n//\n// Note, this will be deprecated in a future release.\n// Prefer to use [fxevent.Logger] instead.\ntype Printer interface {\n\tPrintf(string, ...any)\n}\n\n// Logger redirects the application's log output to the provided printer.\n//\n// Prefer to use [WithLogger] instead.\nfunc Logger(p Printer) Option {\n\treturn loggerOption{p}\n}\n\ntype loggerOption struct{ p Printer }\n\nfunc (l loggerOption) apply(m *module) {\n\tif m.parent != nil {\n\t\tm.app.err = fmt.Errorf(\"fx.Logger Option should be passed to top-level App, \" +\n\t\t\t\"not to fx.Module\")\n\t} else {\n\t\tnp := writerFromPrinter(l.p)\n\t\tm.log = fxlog.DefaultLogger(np) // assuming np is thread-safe.\n\t}\n}\n\nfunc (l loggerOption) String() string {\n\treturn fmt.Sprintf(\"fx.Logger(%v)\", l.p)\n}\n\n// NopLogger disables the application's log output.\n//\n// Note that this makes some failures difficult to debug,\n// since no errors are printed to console.\n// Prefer to log to an in-memory buffer instead.\nvar NopLogger = WithLogger(func() fxevent.Logger { return fxevent.NopLogger })\n\n// An App is a modular application built around dependency injection. Most\n// users will only need to use the New constructor and the all-in-one Run\n// convenience method. In more unusual cases, users may need to use the Err,\n// Start, Done, and Stop methods by hand instead of relying on Run.\n//\n// [New] creates and initializes an App. All applications begin with a\n// constructor for the Lifecycle type already registered.\n//\n// In addition to that built-in functionality, users typically pass a handful\n// of [Provide] options and one or more [Invoke] options. The Provide options\n// teach the application how to instantiate a variety of types, and the Invoke\n// options describe how to initialize the application.\n//\n// When created, the application immediately executes all the functions passed\n// via Invoke options. To supply these functions with the parameters they\n// need, the application looks for constructors that return the appropriate\n// types; if constructors for any required types are missing or any\n// invocations return an error, the application will fail to start (and Err\n// will return a descriptive error message).\n//\n// Once all the invocations (and any required constructors) have been called,\n// New returns and the application is ready to be started using Run or Start.\n// On startup, it executes any OnStart hooks registered with its Lifecycle.\n// OnStart hooks are executed one at a time, in order, and must all complete\n// within a configurable deadline (by default, 15 seconds). For details on the\n// order in which OnStart hooks are executed, see the documentation for the\n// Start method.\n//\n// At this point, the application has successfully started up. If started via\n// Run, it will continue operating until it receives a shutdown signal from\n// Done (see the [App.Done] documentation for details); if started explicitly via\n// Start, it will operate until the user calls Stop. On shutdown, OnStop hooks\n// execute one at a time, in reverse order, and must all complete within a\n// configurable deadline (again, 15 seconds by default).\ntype App struct {\n\terr       error\n\tclock     fxclock.Clock\n\tlifecycle *lifecycleWrapper\n\n\tcontainer *dig.Container\n\troot      *module\n\n\t// Timeouts used\n\tstartTimeout time.Duration\n\tstopTimeout  time.Duration\n\t// Decides how we react to errors when building the graph.\n\terrorHooks []ErrorHandler\n\tvalidate   bool\n\t// Whether to recover from panics in Dig container\n\trecoverFromPanics bool\n\n\t// Used to signal shutdowns.\n\treceivers signalReceivers\n\n\tosExit func(code int) // os.Exit override; used for testing only\n}\n\n// provide is a single constructor provided to Fx.\ntype provide struct {\n\t// Constructor provided to Fx. This may be an fx.Annotated.\n\tTarget any\n\n\t// Stack trace of where this provide was made.\n\tStack fxreflect.Stack\n\n\t// IsSupply is true when the Target constructor was emitted by fx.Supply.\n\tIsSupply   bool\n\tSupplyType reflect.Type // set only if IsSupply\n\n\t// Set if the type should be provided at private scope.\n\tPrivate bool\n}\n\n// invoke is a single invocation request to Fx.\ntype invoke struct {\n\t// Function to invoke.\n\tTarget any\n\n\t// Stack trace of where this invoke was made.\n\tStack fxreflect.Stack\n}\n\n// ErrorHandler handles Fx application startup errors.\n// Register these with [ErrorHook].\n// If specified, and the application fails to start up,\n// the failure will still cause a crash,\n// but you'll have a chance to log the error or take some other action.\ntype ErrorHandler interface {\n\tHandleError(error)\n}\n\n// ErrorHook registers error handlers that implement error handling functions.\n// They are executed on invoke failures. Passing multiple ErrorHandlers appends\n// the new handlers to the application's existing list.\nfunc ErrorHook(funcs ...ErrorHandler) Option {\n\treturn errorHookOption(funcs)\n}\n\ntype errorHookOption []ErrorHandler\n\nfunc (eho errorHookOption) apply(m *module) {\n\tm.app.errorHooks = append(m.app.errorHooks, eho...)\n}\n\nfunc (eho errorHookOption) String() string {\n\titems := make([]string, len(eho))\n\tfor i, eh := range eho {\n\t\titems[i] = fmt.Sprint(eh)\n\t}\n\treturn fmt.Sprintf(\"fx.ErrorHook(%v)\", strings.Join(items, \", \"))\n}\n\ntype errorHandlerList []ErrorHandler\n\nfunc (ehl errorHandlerList) HandleError(err error) {\n\tfor _, eh := range ehl {\n\t\teh.HandleError(err)\n\t}\n}\n\n// validate sets *App into validation mode without running invoked functions.\nfunc validate(validate bool) Option {\n\treturn &validateOption{\n\t\tvalidate: validate,\n\t}\n}\n\ntype validateOption struct {\n\tvalidate bool\n}\n\nfunc (o validateOption) apply(m *module) {\n\tif m.parent != nil {\n\t\tm.app.err = fmt.Errorf(\"fx.validate Option should be passed to top-level App, \" +\n\t\t\t\"not to fx.Module\")\n\t} else {\n\t\tm.app.validate = o.validate\n\t}\n}\n\nfunc (o validateOption) String() string {\n\treturn fmt.Sprintf(\"fx.validate(%v)\", o.validate)\n}\n\n// ValidateApp validates that supplied graph would run and is not missing any dependencies. This\n// method does not invoke actual input functions.\nfunc ValidateApp(opts ...Option) error {\n\topts = append(opts, validate(true))\n\tapp := New(opts...)\n\n\treturn app.Err()\n}\n\n// New creates and initializes an App, immediately executing any functions\n// registered via [Invoke] options. See the documentation of the App struct for\n// details on the application's initialization, startup, and shutdown logic.\nfunc New(opts ...Option) *App {\n\tlogger := fxlog.DefaultLogger(os.Stderr)\n\n\tapp := &App{\n\t\tclock:        fxclock.System,\n\t\tstartTimeout: DefaultTimeout,\n\t\tstopTimeout:  DefaultTimeout,\n\t\treceivers:    newSignalReceivers(),\n\t}\n\tapp.root = &module{\n\t\tapp: app,\n\t\t// We start with a logger that writes to stderr. One of the\n\t\t// following three things can change this:\n\t\t//\n\t\t// - fx.Logger was provided to change the output stream\n\t\t// - fx.WithLogger was provided to change the logger\n\t\t//   implementation\n\t\t// - Both, fx.Logger and fx.WithLogger were provided\n\t\t//\n\t\t// The first two cases are straightforward: we use what the\n\t\t// user gave us. For the last case, however, we need to fall\n\t\t// back to what was provided to fx.Logger if fx.WithLogger\n\t\t// fails.\n\t\tlog:   logger,\n\t\ttrace: []string{fxreflect.CallerStack(1, 2)[0].String()},\n\t}\n\n\tfor _, opt := range opts {\n\t\topt.apply(app.root)\n\t}\n\n\t// There are a few levels of wrapping on the lifecycle here. To quickly\n\t// cover them:\n\t//\n\t// - lifecycleWrapper ensures that we don't unintentionally expose the\n\t//   Start and Stop methods of the internal lifecycle.Lifecycle type\n\t// - lifecycleWrapper also adapts the internal lifecycle.Hook type into\n\t//   the public fx.Hook type.\n\t// - appLogger ensures that the lifecycle always logs events to the\n\t//   \"current\" logger associated with the fx.App.\n\tapp.lifecycle = &lifecycleWrapper{\n\t\tlifecycle.New(appLogger{app}, app.clock),\n\t}\n\n\tcontainerOptions := []dig.Option{\n\t\tdig.DeferAcyclicVerification(),\n\t\tdig.DryRun(app.validate),\n\t}\n\n\tif app.recoverFromPanics {\n\t\tcontainerOptions = append(containerOptions, dig.RecoverFromPanics())\n\t}\n\n\tapp.container = dig.New(containerOptions...)\n\tapp.root.build(app, app.container)\n\n\t// Provide Fx types first to increase the chance a custom logger\n\t// can be successfully built in the face of unrelated DI failure.\n\t// E.g., for a custom logger that relies on the Lifecycle type.\n\tframes := fxreflect.CallerStack(0, 0) // include New in the stack for default Provides\n\tapp.root.provide(provide{\n\t\tTarget: func() Lifecycle { return app.lifecycle },\n\t\tStack:  frames,\n\t})\n\tapp.root.provide(provide{Target: app.shutdowner, Stack: frames})\n\tapp.root.provide(provide{Target: app.dotGraph, Stack: frames})\n\tapp.root.provideAll()\n\n\t// Run decorators before executing any Invokes\n\t// (including the ones inside installAllEventLoggers).\n\tapp.err = multierr.Append(app.err, app.root.decorateAll())\n\n\t// If you are thinking about returning here after provides: do not (just yet)!\n\t// If a custom logger was being used, we're still buffering messages.\n\t// We'll want to flush them to the logger.\n\n\t// custom app logger will be initialized by the root module.\n\tapp.root.installAllEventLoggers()\n\n\t// This error might have come from the provide loop above. We've\n\t// already flushed to the custom logger, so we can return.\n\tif app.err != nil {\n\t\treturn app\n\t}\n\n\tif err := app.root.invokeAll(); err != nil {\n\t\tapp.err = err\n\n\t\tif dig.CanVisualizeError(err) {\n\t\t\tvar b bytes.Buffer\n\t\t\tdig.Visualize(app.container, &b, dig.VisualizeError(err))\n\t\t\terr = errorWithGraph{\n\t\t\t\tgraph: b.String(),\n\t\t\t\terr:   err,\n\t\t\t}\n\t\t}\n\t\terrorHandlerList(app.errorHooks).HandleError(err)\n\t}\n\n\treturn app\n}\n\nfunc (app *App) log() fxevent.Logger {\n\treturn app.root.log\n}\n\n// DotGraph contains a DOT language visualization of the dependency graph in\n// an Fx application. It is provided in the container by default at\n// initialization. On failure to build the dependency graph, it is attached\n// to the error and if possible, colorized to highlight the root cause of the\n// failure.\n//\n// Note that DotGraph does not yet recognize [Decorate] and [Replace].\ntype DotGraph string\n\ntype errWithGraph interface {\n\tGraph() DotGraph\n}\n\ntype errorWithGraph struct {\n\tgraph string\n\terr   error\n}\n\nfunc (err errorWithGraph) Graph() DotGraph {\n\treturn DotGraph(err.graph)\n}\n\nfunc (err errorWithGraph) Error() string {\n\treturn err.err.Error()\n}\n\n// VisualizeError returns the visualization of the error if available.\n//\n// Note that VisualizeError does not yet recognize [Decorate] and [Replace].\nfunc VisualizeError(err error) (string, error) {\n\tvar erg errWithGraph\n\tif errors.As(err, &erg) {\n\t\tif g := erg.Graph(); g != \"\" {\n\t\t\treturn string(g), nil\n\t\t}\n\t}\n\treturn \"\", errors.New(\"unable to visualize error\")\n}\n\n// Exits the application with the given exit code.\nfunc (app *App) exit(code int) {\n\tosExit := os.Exit\n\tif app.osExit != nil {\n\t\tosExit = app.osExit\n\t}\n\tosExit(code)\n}\n\n// Run starts the application, blocks on the signals channel, and then\n// gracefully shuts the application down. It uses [DefaultTimeout] to set a\n// deadline for application startup and shutdown, unless the user has\n// configured different timeouts with the [StartTimeout] or [StopTimeout] options.\n// It's designed to make typical applications simple to run.\n// The minimal Fx application looks like this:\n//\n//\tfx.New().Run()\n//\n// All of Run's functionality is implemented in terms of the exported\n// Start, Done, and Stop methods. Applications with more specialized needs\n// can use those methods directly instead of relying on Run.\n//\n// After the application has started,\n// it can be shut down by sending a signal or calling [Shutdowner.Shutdown].\n// On successful shutdown, whether initiated by a signal or by the user,\n// Run will return to the caller, allowing it to exit cleanly.\n// Run will exit with a non-zero status code\n// if startup or shutdown operations fail,\n// or if the [Shutdowner] supplied a non-zero exit code.\nfunc (app *App) Run() {\n\t// Historically, we do not os.Exit(0) even though most applications\n\t// cede control to Fx with they call app.Run. To avoid a breaking\n\t// change, never os.Exit for success.\n\tif code := app.run(app.Wait); code != 0 {\n\t\tapp.exit(code)\n\t}\n}\n\nfunc (app *App) run(done func() <-chan ShutdownSignal) (exitCode int) {\n\tstartCtx, cancel := app.clock.WithTimeout(context.Background(), app.StartTimeout())\n\tdefer cancel()\n\n\tif err := app.Start(startCtx); err != nil {\n\t\treturn 1\n\t}\n\n\tsig := <-done()\n\tapp.log().LogEvent(&fxevent.Stopping{Signal: sig.Signal})\n\texitCode = sig.ExitCode\n\n\tstopCtx, cancel := app.clock.WithTimeout(context.Background(), app.StopTimeout())\n\tdefer cancel()\n\n\tif err := app.Stop(stopCtx); err != nil {\n\t\treturn 1\n\t}\n\n\treturn exitCode\n}\n\n// Err returns any error encountered during New's initialization. See the\n// documentation of the New method for details, but typical errors include\n// missing constructors, circular dependencies, constructor errors, and\n// invocation errors.\n//\n// Most users won't need to use this method, since both Run and Start\n// short-circuit if initialization failed.\nfunc (app *App) Err() error {\n\treturn app.err\n}\n\nvar (\n\t_onStartHook = \"OnStart\"\n\t_onStopHook  = \"OnStop\"\n)\n\n// Start kicks off all long-running goroutines, like network servers or\n// message queue consumers. It does this by interacting with the application's\n// Lifecycle.\n//\n// By taking a dependency on the Lifecycle type, some of the user-supplied\n// functions called during initialization may have registered start and stop\n// hooks. Because initialization calls constructors serially and in dependency\n// order, hooks are naturally registered in serial and dependency order too.\n//\n// Start executes all OnStart hooks registered with the application's\n// Lifecycle, one at a time and in order. This ensures that each constructor's\n// start hooks aren't executed until all its dependencies' start hooks\n// complete. If any of the start hooks return an error, Start short-circuits,\n// calls Stop, and returns the inciting error.\n//\n// Note that Start short-circuits immediately if the New constructor\n// encountered any errors in application initialization.\nfunc (app *App) Start(ctx context.Context) (err error) {\n\tdefer func() {\n\t\tapp.log().LogEvent(&fxevent.Started{Err: err})\n\t}()\n\n\tif app.err != nil {\n\t\t// Some provides failed, short-circuit immediately.\n\t\treturn app.err\n\t}\n\n\treturn withTimeout(ctx, &withTimeoutParams{\n\t\thook:      _onStartHook,\n\t\tcallback:  app.start,\n\t\tlifecycle: app.lifecycle,\n\t\tlog:       app.log(),\n\t})\n}\n\n// withRollback will execute an anonymous function with a given context.\n// if the anon func returns an error, rollback methods will be called and related events emitted\nfunc (app *App) withRollback(\n\tctx context.Context,\n\tf func(context.Context) error,\n) error {\n\tif err := f(ctx); err != nil {\n\t\tapp.log().LogEvent(&fxevent.RollingBack{StartErr: err})\n\n\t\tstopErr := app.lifecycle.Stop(ctx)\n\t\tapp.log().LogEvent(&fxevent.RolledBack{Err: stopErr})\n\n\t\tif stopErr != nil {\n\t\t\treturn multierr.Append(err, stopErr)\n\t\t}\n\n\t\treturn err\n\t}\n\n\treturn nil\n}\n\nfunc (app *App) start(ctx context.Context) error {\n\treturn app.withRollback(ctx, func(ctx context.Context) error {\n\t\tif err := app.lifecycle.Start(ctx); err != nil {\n\t\t\treturn err\n\t\t}\n\t\treturn nil\n\t})\n}\n\n// Stop gracefully stops the application. It executes any registered OnStop\n// hooks in reverse order, so that each constructor's stop hooks are called\n// before its dependencies' stop hooks.\n//\n// If the application didn't start cleanly, only hooks whose OnStart phase was\n// called are executed. However, all those hooks are executed, even if some\n// fail.\nfunc (app *App) Stop(ctx context.Context) (err error) {\n\tdefer func() {\n\t\tapp.log().LogEvent(&fxevent.Stopped{Err: err})\n\t}()\n\n\tcb := func(ctx context.Context) error {\n\t\tdefer app.receivers.Stop(ctx)\n\t\treturn app.lifecycle.Stop(ctx)\n\t}\n\n\treturn withTimeout(ctx, &withTimeoutParams{\n\t\thook:      _onStopHook,\n\t\tcallback:  cb,\n\t\tlifecycle: app.lifecycle,\n\t\tlog:       app.log(),\n\t})\n}\n\n// Done returns a channel of signals to block on after starting the\n// application. Applications listen for the SIGINT and SIGTERM signals; during\n// development, users can send the application SIGTERM by pressing Ctrl-C in\n// the same terminal as the running process.\n//\n// Alternatively, a signal can be broadcast to all done channels manually by\n// using the Shutdown functionality (see the [Shutdowner] documentation for details).\nfunc (app *App) Done() <-chan os.Signal {\n\tapp.receivers.Start() // No-op if running\n\treturn app.receivers.Done()\n}\n\n// Wait returns a channel of [ShutdownSignal] to block on after starting the\n// application and function, similar to [App.Done], but with a minor difference:\n// if the app was shut down via [Shutdowner.Shutdown],\n// the exit code (if provied via [ExitCode]) will be available\n// in the [ShutdownSignal] struct.\n// Otherwise, the signal that was received will be set.\nfunc (app *App) Wait() <-chan ShutdownSignal {\n\tapp.receivers.Start() // No-op if running\n\treturn app.receivers.Wait()\n}\n\n// StartTimeout returns the configured startup timeout.\n// This defaults to [DefaultTimeout], and can be changed with the\n// [StartTimeout] option.\nfunc (app *App) StartTimeout() time.Duration {\n\treturn app.startTimeout\n}\n\n// StopTimeout returns the configured shutdown timeout.\n// This defaults to [DefaultTimeout], and can be changed with the\n// [StopTimeout] option.\nfunc (app *App) StopTimeout() time.Duration {\n\treturn app.stopTimeout\n}\n\nfunc (app *App) dotGraph() (DotGraph, error) {\n\tvar b bytes.Buffer\n\terr := dig.Visualize(app.container, &b)\n\treturn DotGraph(b.String()), err\n}\n\ntype withTimeoutParams struct {\n\tlog       fxevent.Logger\n\thook      string\n\tcallback  func(context.Context) error\n\tlifecycle *lifecycleWrapper\n}\n\n// errHookCallbackExited is returned when a hook callback does not finish executing\nvar errHookCallbackExited = errors.New(\"goroutine exited without returning\")\n\nfunc withTimeout(ctx context.Context, param *withTimeoutParams) error {\n\tc := make(chan error, 1)\n\tgo func() {\n\t\t// If runtime.Goexit() is called from within the callback\n\t\t// then nothing is written to the chan.\n\t\t// However the defer will still be called, so we can write to the chan,\n\t\t// to avoid hanging until the timeout is reached.\n\t\tcallbackExited := false\n\t\tdefer func() {\n\t\t\tif !callbackExited {\n\t\t\t\tc <- errHookCallbackExited\n\t\t\t}\n\t\t}()\n\n\t\tc <- param.callback(ctx)\n\t\tcallbackExited = true\n\t}()\n\n\tvar err error\n\n\tselect {\n\tcase <-ctx.Done():\n\t\terr = ctx.Err()\n\tcase err = <-c:\n\t\t// If the context finished at the same time as the callback\n\t\t// prefer the context error.\n\t\t// This eliminates non-determinism in select-case selection.\n\t\tif ctx.Err() != nil {\n\t\t\terr = ctx.Err()\n\t\t}\n\t}\n\n\treturn err\n}\n\n// appLogger logs events to the given Fx app's \"current\" logger.\n//\n// Use this with lifecycle, for example, to ensure that events always go to the\n// correct logger.\ntype appLogger struct{ app *App }\n\nfunc (l appLogger) LogEvent(ev fxevent.Event) {\n\tl.app.log().LogEvent(ev)\n}\n"
  },
  {
    "path": "app_internal_test.go",
    "content": "// Copyright (c) 2019-2021 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage fx\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"os\"\n\t\"sync\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\t\"go.uber.org/fx/fxevent\"\n\t\"go.uber.org/fx/internal/fxclock\"\n\t\"go.uber.org/fx/internal/fxlog\"\n\t\"go.uber.org/fx/internal/fxreflect\"\n)\n\nfunc TestAppRun(t *testing.T) {\n\tt.Parallel()\n\n\tspy := new(fxlog.Spy)\n\tapp := New(\n\t\tWithLogger(func() fxevent.Logger { return spy }),\n\t)\n\tdone := make(chan ShutdownSignal)\n\n\tvar wg sync.WaitGroup\n\twg.Add(1)\n\tgo func() {\n\t\tdefer wg.Done()\n\t\tapp.run(func() <-chan ShutdownSignal { return done })\n\t}()\n\n\tdone <- ShutdownSignal{Signal: _sigINT}\n\twg.Wait()\n\n\tassert.Equal(t, []string{\n\t\t\"Provided\",\n\t\t\"Provided\",\n\t\t\"Provided\",\n\t\t\"LoggerInitialized\",\n\t\t\"Started\",\n\t\t\"Stopping\",\n\t\t\"Stopped\",\n\t}, spy.EventTypes())\n}\n\n// TestValidateString verifies private option. Public options are tested in app_test.go.\nfunc TestValidateString(t *testing.T) {\n\tt.Parallel()\n\n\tstringer, ok := validate(true).(fmt.Stringer)\n\trequire.True(t, ok, \"option must implement stringer\")\n\tassert.Equal(t, \"fx.validate(true)\", stringer.String())\n}\n\n// WithExit is an internal option available only to tests defined in this\n// package. It changes how os.Exit behaves for the application.\nfunc WithExit(f func(int)) Option {\n\treturn withExitOption(f)\n}\n\ntype withExitOption func(int)\n\nfunc (o withExitOption) String() string {\n\treturn fmt.Sprintf(\"WithExit(%v)\", fxreflect.FuncName(o))\n}\n\nfunc (o withExitOption) apply(m *module) {\n\tm.app.osExit = o\n}\n\n// WithClock specifies how Fx accesses time operations.\n//\n// This is an internal option available only to tests defined in this package.\nfunc WithClock(clock fxclock.Clock) Option {\n\treturn withClockOption{clock}\n}\n\ntype withClockOption struct{ clock fxclock.Clock }\n\nfunc (o withClockOption) apply(m *module) {\n\tm.app.clock = o.clock\n}\n\nfunc (o withClockOption) String() string {\n\treturn fmt.Sprintf(\"WithClock(%v)\", o.clock)\n}\n\nfunc TestAnnotationError(t *testing.T) {\n\twantErr := errors.New(\"want error\")\n\terr := &annotationError{\n\t\terr: wantErr,\n\t}\n\trequire.Error(t, err)\n\tassert.ErrorIs(t, err, wantErr)\n\tassert.Contains(t, err.Error(), wantErr.Error())\n}\n\n// TestStartDoesNotRegisterSignals verifies that signal.Notify is not called\n// when a user starts an app. signal.Notify should only be called when the\n// .Wait/.Done are called. Note that app.Run calls .Wait() implicitly.\nfunc TestStartDoesNotRegisterSignals(t *testing.T) {\n\tapp := New()\n\tcalledNotify := false\n\n\t// Mock notify function to spy when this is called.\n\tapp.receivers.notify = func(c chan<- os.Signal, sig ...os.Signal) {\n\t\tcalledNotify = true\n\t}\n\tapp.receivers.stopNotify = func(c chan<- os.Signal) {}\n\n\tapp.Start(context.Background())\n\tdefer app.Stop(context.Background())\n\tassert.False(t, calledNotify, \"notify should not be called when app starts\")\n\n\t_ = app.Wait() // User signals intent have fx listen for signals. This should call notify\n\tassert.True(t, calledNotify, \"notify should be called after Wait\")\n}\n"
  },
  {
    "path": "app_test.go",
    "content": "// Copyright (c) 2023 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage fx_test\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"log\"\n\t\"os\"\n\t\"reflect\"\n\t\"regexp\"\n\t\"runtime\"\n\t\"strings\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\n\t. \"go.uber.org/fx\"\n\t\"go.uber.org/fx/fxevent\"\n\t\"go.uber.org/fx/fxtest\"\n\t\"go.uber.org/fx/internal/fxclock\"\n\t\"go.uber.org/fx/internal/fxlog\"\n\t\"go.uber.org/goleak\"\n\t\"go.uber.org/multierr\"\n\t\"go.uber.org/zap\"\n)\n\nfunc NewForTest(tb testing.TB, opts ...Option) *App {\n\ttestOpts := []Option{\n\t\t// Provide both: Logger and WithLogger so that if the test\n\t\t// WithLogger fails, we don't pollute stderr.\n\t\tLogger(fxtest.NewTestPrinter(tb)),\n\t\tfxtest.WithTestLogger(tb),\n\t}\n\topts = append(testOpts, opts...)\n\n\treturn New(opts...)\n}\n\nfunc NewSpied(opts ...Option) (*App, *fxlog.Spy) {\n\tspy := new(fxlog.Spy)\n\topts = append([]Option{\n\t\tWithLogger(func() fxevent.Logger { return spy }),\n\t}, opts...)\n\treturn New(opts...), spy\n}\n\nfunc validateTestApp(tb testing.TB, opts ...Option) error {\n\ttestOpts := []Option{\n\t\t// Provide both: Logger and WithLogger so that if the test\n\t\t// WithLogger fails, we don't pollute stderr.\n\t\tLogger(fxtest.NewTestPrinter(tb)),\n\t\tfxtest.WithTestLogger(tb),\n\t}\n\topts = append(testOpts, opts...)\n\n\treturn ValidateApp(opts...)\n}\n\nfunc TestNewApp(t *testing.T) {\n\tt.Parallel()\n\n\tt.Run(\"ProvidesLifecycleAndShutdowner\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tvar (\n\t\t\tl Lifecycle\n\t\t\ts Shutdowner\n\t\t)\n\t\tfxtest.New(\n\t\t\tt,\n\t\t\tPopulate(&l, &s),\n\t\t)\n\t\tassert.NotNil(t, l)\n\t\tassert.NotNil(t, s)\n\t})\n\n\tt.Run(\"OptionsHappensBeforeProvides\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\t// Should be grouping all provides and pushing them into the container\n\t\t// after applying other options. This prevents the app configuration\n\t\t// (e.g., logging) from changing halfway through our provides.\n\n\t\tspy := new(fxlog.Spy)\n\t\tapp := fxtest.New(t, Provide(func() struct{} { return struct{}{} }),\n\t\t\tWithLogger(func() fxevent.Logger { return spy }))\n\t\tdefer app.RequireStart().RequireStop()\n\t\trequire.Equal(t,\n\t\t\t[]string{\"Provided\", \"Provided\", \"Provided\", \"Provided\", \"LoggerInitialized\", \"Started\"},\n\t\t\tspy.EventTypes())\n\n\t\t// Fx types get provided first to increase chance of\n\t\t// successful custom logger build.\n\t\tassert.Contains(t, spy.Events()[0].(*fxevent.Provided).OutputTypeNames, \"fx.Lifecycle\")\n\t\tassert.Contains(t, spy.Events()[1].(*fxevent.Provided).OutputTypeNames, \"fx.Shutdowner\")\n\t\tassert.Contains(t, spy.Events()[2].(*fxevent.Provided).OutputTypeNames, \"fx.DotGraph\")\n\t\t// Our type should be index 3.\n\t\tassert.Contains(t, spy.Events()[3].(*fxevent.Provided).OutputTypeNames, \"struct {}\")\n\t})\n\n\tt.Run(\"CircularGraphReturnsError\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\ttype A struct{}\n\t\ttype B struct{}\n\t\tapp := NewForTest(t,\n\t\t\tProvide(func(A) B { return B{} }),\n\t\t\tProvide(func(B) A { return A{} }),\n\t\t\tInvoke(func(B) {}),\n\t\t)\n\t\terr := app.Err()\n\t\trequire.Error(t, err, \"fx.New should return an error\")\n\n\t\terrMsg := err.Error()\n\t\tassert.Contains(t, errMsg, \"cycle detected in dependency graph\")\n\t\tassert.Contains(t, errMsg, \"depends on func(fx_test.B) fx_test.A\")\n\t\tassert.Contains(t, errMsg, \"depends on func(fx_test.A) fx_test.B\")\n\t})\n\n\tt.Run(\"ProvidesDotGraph\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\ttype A struct{}\n\t\ttype B struct{}\n\t\ttype C struct{}\n\t\tvar g DotGraph\n\t\tapp := fxtest.New(t,\n\t\t\tProvide(func() A { return A{} }),\n\t\t\tProvide(func(A) B { return B{} }),\n\t\t\tProvide(func(A, B) C { return C{} }),\n\t\t\tPopulate(&g),\n\t\t)\n\t\tdefer app.RequireStart().RequireStop()\n\t\trequire.NoError(t, app.Err())\n\t\tassert.Contains(t, g, `\"fx.DotGraph\" [label=<fx.DotGraph>];`)\n\t})\n\n\tt.Run(\"ProvidesWithAnnotate\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\ttype A struct{}\n\n\t\ttype B struct {\n\t\t\tIn\n\n\t\t\tFoo  A   `name:\"foo\"`\n\t\t\tBar  A   `name:\"bar\"`\n\t\t\tFoos []A `group:\"foo\"`\n\t\t}\n\n\t\tapp := fxtest.New(t,\n\t\t\tProvide(\n\t\t\t\tAnnotated{\n\t\t\t\t\tTarget: func() A { return A{} },\n\t\t\t\t\tName:   \"foo\",\n\t\t\t\t},\n\t\t\t\tAnnotated{\n\t\t\t\t\tTarget: func() A { return A{} },\n\t\t\t\t\tName:   \"bar\",\n\t\t\t\t},\n\t\t\t\tAnnotated{\n\t\t\t\t\tTarget: func() A { return A{} },\n\t\t\t\t\tGroup:  \"foo\",\n\t\t\t\t},\n\t\t\t),\n\t\t\tInvoke(\n\t\t\t\tfunc(b B) {\n\t\t\t\t\tassert.NotNil(t, b.Foo)\n\t\t\t\t\tassert.NotNil(t, b.Bar)\n\t\t\t\t\tassert.Len(t, b.Foos, 1)\n\t\t\t\t},\n\t\t\t),\n\t\t)\n\n\t\tdefer app.RequireStart().RequireStop()\n\t\trequire.NoError(t, app.Err())\n\t})\n\n\tt.Run(\"ProvidesWithAnnotateFlattened\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tapp := fxtest.New(t,\n\t\t\tProvide(Annotated{\n\t\t\t\tTarget: func() []int { return []int{1} },\n\t\t\t\tGroup:  \"foo,flatten\",\n\t\t\t}),\n\t\t\tInvoke(\n\t\t\t\tfunc(b struct {\n\t\t\t\t\tIn\n\t\t\t\t\tFoos []int `group:\"foo\"`\n\t\t\t\t},\n\t\t\t\t) {\n\t\t\t\t\tassert.Len(t, b.Foos, 1)\n\t\t\t\t},\n\t\t\t),\n\t\t)\n\n\t\tdefer app.RequireStart().RequireStop()\n\t\trequire.NoError(t, app.Err())\n\t})\n\n\tt.Run(\"ProvidesWithEmptyAnnotate\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\ttype A struct{}\n\n\t\ttype B struct {\n\t\t\tIn\n\n\t\t\tFoo A\n\t\t}\n\n\t\tapp := fxtest.New(t,\n\t\t\tProvide(\n\t\t\t\tAnnotated{\n\t\t\t\t\tTarget: func() A { return A{} },\n\t\t\t\t},\n\t\t\t),\n\t\t\tInvoke(\n\t\t\t\tfunc(b B) {\n\t\t\t\t\tassert.NotNil(t, b.Foo)\n\t\t\t\t},\n\t\t\t),\n\t\t)\n\n\t\tdefer app.RequireStart().RequireStop()\n\t\trequire.NoError(t, app.Err())\n\t})\n\n\tt.Run(\"CannotNameAndGroup\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\ttype A struct{}\n\n\t\tapp := NewForTest(t,\n\t\t\tProvide(\n\t\t\t\tAnnotated{\n\t\t\t\t\tTarget: func() A { return A{} },\n\t\t\t\t\tName:   \"foo\",\n\t\t\t\t\tGroup:  \"bar\",\n\t\t\t\t},\n\t\t\t),\n\t\t)\n\n\t\terr := app.Err()\n\t\trequire.Error(t, err)\n\n\t\t// fx.Annotated may specify only one of Name or Group: received fx.Annotated{Name: \"foo\", Group: \"bar\", Target: go.uber.org/fx_test.TestAnnotatedWithGroupAndName.func1()} from:\n\t\t// go.uber.org/fx_test.TestAnnotatedWithGroupAndName\n\t\t//         /.../fx/annotated_test.go:164\n\t\t// testing.tRunner\n\t\t//         /.../go/1.13.3/libexec/src/testing/testing.go:909\n\t\tassert.Contains(t, err.Error(), \"fx.Annotated may specify only one of Name or Group:\")\n\t\tassert.Contains(t, err.Error(), `received fx.Annotated{Name: \"foo\", Group: \"bar\", Target: go.uber.org/fx_test.TestNewApp`)\n\t\tassert.Contains(t, err.Error(), \"go.uber.org/fx_test.TestNewApp\")\n\t\tassert.Contains(t, err.Error(), \"/app_test.go\")\n\t})\n\n\tt.Run(\"ErrorProvidingAnnotated\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tapp := NewForTest(t, Provide(Annotated{\n\t\t\tTarget: 42, // not a constructor\n\t\t\tName:   \"foo\",\n\t\t}))\n\n\t\terr := app.Err()\n\t\trequire.Error(t, err)\n\n\t\t// Example:\n\t\t// fx.Provide(fx.Annotated{...}) from:\n\t\t//     go.uber.org/fx_test.TestNewApp.func8\n\t\t//         /.../fx/app_test.go:206\n\t\t//     testing.tRunner\n\t\t//         /.../go/1.13.3/libexec/src/testing/testing.go:909\n\t\t//     Failed: must provide constructor function, got 42 (type int)\n\t\tassert.Contains(t, err.Error(), `fx.Provide(fx.Annotated{Name: \"foo\", Target: 42}) from:`)\n\t\tassert.Contains(t, err.Error(), \"go.uber.org/fx_test.TestNewApp\")\n\t\tassert.Contains(t, err.Error(), \"/app_test.go\")\n\t\tassert.Contains(t, err.Error(), \"Failed: must provide constructor function\")\n\t})\n\n\tt.Run(\"ErrorProvidingAnnotate\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\ttype t1 struct{}\n\t\tnewT1 := func() t1 { return t1{} }\n\n\t\t// Provide twice.\n\t\tapp := NewForTest(t, Provide(\n\t\t\tAnnotate(newT1, ResultTags(`name:\"foo\"`)),\n\t\t\tAnnotate(newT1, ResultTags(`name:\"foo\"`)),\n\t\t))\n\n\t\terr := app.Err()\n\t\trequire.Error(t, err)\n\n\t\t// Example:\n\t\t// fx.Provide(fx.Annotate(go.uber.org/fx_test.TestNewApp.func10.1(), fx.ResultTags([\"name:\\\"foo\\\"\"])) from:\n\t\t//     go.uber.org/fx_test.TestNewApp.func10\n\t\t//         /.../fx/app_test.go:305\n\t\t//     testing.tRunner\n\t\t//         /.../src/testing/testing.go:1259\n\t\t//     Failed: cannot provide function \"reflect\".makeFuncStub (/.../reflect/asm_amd64.s:30):\n\t\t//     cannot provide fx_test.t1[name=\"foo\"] from [0].Field0:\n\t\t//     already provided by \"reflect\".makeFuncStub (/.../reflect/asm_amd64.s:30)\n\t\tassert.Contains(t, err.Error(), `fx.Provide(fx.Annotate(`)\n\t\tassert.Contains(t, err.Error(), `fx.ResultTags([\"name:\\\"foo\\\"\"])`)\n\t\tassert.Contains(t, err.Error(), \"already provided\")\n\t})\n\n\tt.Run(\"ErrorProviding\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\terr := NewForTest(t, Provide(42)).Err()\n\t\trequire.Error(t, err)\n\n\t\t// Example:\n\t\t// fx.Provide(..) from:\n\t\t//     go.uber.org/fx_test.TestNewApp.func8\n\t\t//         /.../fx/app_test.go:206\n\t\t//     testing.tRunner\n\t\t//         /.../go/1.13.3/libexec/src/testing/testing.go:909\n\t\t//     Failed: must provide constructor function, got 42 (type int)\n\t\tassert.Contains(t, err.Error(), \"fx.Provide(42) from:\")\n\t\tassert.Contains(t, err.Error(), \"go.uber.org/fx_test.TestNewApp\")\n\t\tassert.Contains(t, err.Error(), \"/app_test.go\")\n\t\tassert.Contains(t, err.Error(), \"Failed: must provide constructor function\")\n\t})\n\n\tt.Run(\"Decorates\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tspy := new(fxlog.Spy)\n\n\t\ttype A struct{ value int }\n\t\tapp := fxtest.New(t,\n\t\t\tProvide(func() A { return A{value: 0} }),\n\t\t\tDecorate(func(a A) A { return A{value: a.value + 1} }),\n\t\t\tInvoke(func(a A) { assert.Equal(t, a.value, 1) }),\n\t\t\tWithLogger(func() fxevent.Logger { return spy }))\n\t\tdefer app.RequireStart().RequireStop()\n\n\t\trequire.Equal(t,\n\t\t\t[]string{\"Provided\", \"Provided\", \"Provided\", \"Provided\", \"Decorated\", \"LoggerInitialized\", \"Invoking\", \"BeforeRun\", \"Run\", \"BeforeRun\", \"Run\", \"Invoked\", \"Started\"},\n\t\t\tspy.EventTypes())\n\t})\n\n\tt.Run(\"DecoratesFromManyModules\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tspy := new(fxlog.Spy)\n\n\t\ttype A struct{ value int }\n\t\tm := Module(\"decorator\",\n\t\t\tDecorate(func(a A) A { return A{value: a.value + 1} }),\n\t\t)\n\t\tapp := fxtest.New(t,\n\t\t\tm,\n\t\t\tProvide(func() A { return A{value: 0} }),\n\t\t\tDecorate(func(a A) A { return A{value: a.value + 1} }),\n\t\t\tWithLogger(func() fxevent.Logger { return spy }),\n\t\t)\n\t\tdefer app.RequireStart().RequireStop()\n\n\t\trequire.Equal(t,\n\t\t\t[]string{\"Provided\", \"Provided\", \"Provided\", \"Provided\", \"Decorated\", \"Decorated\", \"LoggerInitialized\", \"Started\"},\n\t\t\tspy.EventTypes())\n\t})\n}\n\n// TestPrivate tests Private when used with both fx.Provide and fx.Supply.\nfunc TestPrivate(t *testing.T) {\n\tt.Parallel()\n\n\ttestCases := []struct {\n\t\tdesc string\n\n\t\t// provide is either a Supply or Provide wrapper around the given int\n\t\t// that allows us to generalize these test cases for both APIs.\n\t\tprovide func(int, bool) Option\n\t}{\n\t\t{\n\t\t\tdesc: \"Provide\",\n\t\t\tprovide: func(i int, private bool) Option {\n\t\t\t\topts := []any{func() int { return i }}\n\t\t\t\tif private {\n\t\t\t\t\topts = append(opts, Private)\n\t\t\t\t}\n\t\t\t\treturn Provide(opts...)\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tdesc: \"Supply\",\n\t\t\tprovide: func(i int, private bool) Option {\n\t\t\t\topts := []any{i}\n\t\t\t\tif private {\n\t\t\t\t\topts = append(opts, Private)\n\t\t\t\t}\n\t\t\t\treturn Supply(opts...)\n\t\t\t},\n\t\t},\n\t}\n\n\tfor _, tt := range testCases {\n\t\tt.Run(tt.desc, func(t *testing.T) {\n\t\t\tt.Parallel()\n\n\t\t\tt.Run(\"CanUsePrivateFromParentModule\", func(t *testing.T) {\n\t\t\t\tt.Parallel()\n\n\t\t\t\tvar invoked bool\n\t\t\t\tapp := fxtest.New(t,\n\t\t\t\t\tModule(\"SubModule\", Invoke(func(a int, b string) {\n\t\t\t\t\t\tassert.Equal(t, 0, a)\n\t\t\t\t\t\tinvoked = true\n\t\t\t\t\t})),\n\t\t\t\t\tProvide(func() string { return \"\" }),\n\t\t\t\t\ttt.provide(0, true /* private */),\n\t\t\t\t)\n\t\t\t\tapp.RequireStart().RequireStop()\n\t\t\t\tassert.True(t, invoked)\n\t\t\t})\n\n\t\t\tt.Run(\"CannotUsePrivateFromSubModule\", func(t *testing.T) {\n\t\t\t\tt.Parallel()\n\n\t\t\t\tapp := NewForTest(t,\n\t\t\t\t\tModule(\"SubModule\", tt.provide(0, true /* private */)),\n\t\t\t\t\tInvoke(func(a int) {}),\n\t\t\t\t)\n\t\t\t\terr := app.Err()\n\t\t\t\trequire.Error(t, err)\n\t\t\t\tassert.Contains(t, err.Error(), \"missing dependencies for function\")\n\t\t\t\tassert.Contains(t, err.Error(), \"missing type: int\")\n\t\t\t})\n\n\t\t\tt.Run(\"MultipleModulesSameType\", func(t *testing.T) {\n\t\t\t\tt.Parallel()\n\n\t\t\t\tvar invoked int\n\t\t\t\tapp := fxtest.New(t,\n\t\t\t\t\tModule(\"SubModuleA\",\n\t\t\t\t\t\ttt.provide(1, true /* private */),\n\t\t\t\t\t\tInvoke(func(s int) {\n\t\t\t\t\t\t\tassert.Equal(t, 1, s)\n\t\t\t\t\t\t\tinvoked++\n\t\t\t\t\t\t}),\n\t\t\t\t\t),\n\t\t\t\t\tModule(\"SubModuleB\",\n\t\t\t\t\t\ttt.provide(2, true /* private */),\n\t\t\t\t\t\tInvoke(func(s int) {\n\t\t\t\t\t\t\tassert.Equal(t, 2, s)\n\t\t\t\t\t\t\tinvoked++\n\t\t\t\t\t\t}),\n\t\t\t\t\t),\n\t\t\t\t\ttt.provide(3, false /* private */),\n\t\t\t\t\tInvoke(func(s int) {\n\t\t\t\t\t\tassert.Equal(t, 3, s)\n\t\t\t\t\t\tinvoked++\n\t\t\t\t\t}),\n\t\t\t\t)\n\t\t\t\tapp.RequireStart().RequireStop()\n\t\t\t\tassert.Equal(t, 3, invoked)\n\t\t\t})\n\t\t})\n\t}\n}\n\nfunc TestPrivateProvideWithDecorators(t *testing.T) {\n\tt.Parallel()\n\n\ttestCases := []struct {\n\t\tdesc string\n\n\t\t// provide is either a Supply or Provide wrapper around the given int\n\t\t// that allows us to generalize these test cases for both APIs.\n\t\tprovide func(int) Option\n\t\tprivate bool\n\t}{\n\t\t{\n\t\t\tdesc: \"Private/Provide\",\n\t\t\tprovide: func(i int) Option {\n\t\t\t\treturn Provide(\n\t\t\t\t\tfunc() int { return i },\n\t\t\t\t\tPrivate,\n\t\t\t\t)\n\t\t\t},\n\t\t\tprivate: true,\n\t\t},\n\t\t{\n\t\t\tdesc: \"Private/Supply\",\n\t\t\tprovide: func(i int) Option {\n\t\t\t\treturn Supply(i, Private)\n\t\t\t},\n\t\t\tprivate: true,\n\t\t},\n\t\t{\n\t\t\tdesc: \"Public/Provide\",\n\t\t\tprovide: func(i int) Option {\n\t\t\t\treturn Provide(func() int { return i })\n\t\t\t},\n\t\t\tprivate: false,\n\t\t},\n\t\t{\n\t\t\tdesc: \"Public/Supply\",\n\t\t\tprovide: func(i int) Option {\n\t\t\t\treturn Supply(i)\n\t\t\t},\n\t\t\tprivate: false,\n\t\t},\n\t}\n\n\tfor _, tt := range testCases {\n\t\tt.Run(tt.desc, func(t *testing.T) {\n\t\t\tt.Parallel()\n\n\t\t\tt.Run(\"DecoratedTypeInSubModule\", func(t *testing.T) {\n\t\t\t\tt.Parallel()\n\n\t\t\t\tvar invoked bool\n\t\t\t\tapp := NewForTest(t,\n\t\t\t\t\tModule(\"SubModule\",\n\t\t\t\t\t\ttt.provide(0),\n\t\t\t\t\t\tDecorate(func(a int) int { return a + 2 }),\n\t\t\t\t\t\tInvoke(func(a int) { assert.Equal(t, 2, a) }),\n\t\t\t\t\t),\n\t\t\t\t\tInvoke(func(a int) {\n\t\t\t\t\t\t// Decoration is always \"private\",\n\t\t\t\t\t\t// so raw provided value is expected here\n\t\t\t\t\t\t// when the submodule provides it as public.\n\t\t\t\t\t\tassert.Equal(t, 0, a)\n\t\t\t\t\t\tinvoked = true\n\t\t\t\t\t}),\n\t\t\t\t)\n\t\t\t\terr := app.Err()\n\t\t\t\tif tt.private {\n\t\t\t\t\trequire.Error(t, err)\n\t\t\t\t\tassert.Contains(t, err.Error(), \"missing dependencies for function\")\n\t\t\t\t\tassert.Contains(t, err.Error(), \"missing type: int\")\n\t\t\t\t} else {\n\t\t\t\t\trequire.NoError(t, err)\n\t\t\t\t\tassert.True(t, invoked)\n\t\t\t\t}\n\t\t\t})\n\n\t\t\tt.Run(\"DecoratedTypeInParentModule\", func(t *testing.T) {\n\t\t\t\tt.Parallel()\n\n\t\t\t\tvar invoked int\n\t\t\t\tapp := fxtest.New(t,\n\t\t\t\t\ttt.provide(0),\n\t\t\t\t\tDecorate(func(a int) int { return a - 5 }),\n\t\t\t\t\tInvoke(func(a int) {\n\t\t\t\t\t\tassert.Equal(t, -5, a)\n\t\t\t\t\t\tinvoked++\n\t\t\t\t\t}),\n\t\t\t\t\tModule(\"Child\",\n\t\t\t\t\t\tDecorate(func(a int) int { return a + 10 }),\n\t\t\t\t\t\tInvoke(func(a int) {\n\t\t\t\t\t\t\tassert.Equal(t, 5, a)\n\t\t\t\t\t\t\tinvoked++\n\t\t\t\t\t\t}),\n\t\t\t\t\t),\n\t\t\t\t)\n\t\t\t\tapp.RequireStart().RequireStop()\n\t\t\t\tassert.Equal(t, 2, invoked)\n\t\t\t})\n\n\t\t\tt.Run(\"ParentDecorateChildType\", func(t *testing.T) {\n\t\t\t\tt.Parallel()\n\n\t\t\t\tvar invoked bool\n\t\t\t\tapp := NewForTest(t,\n\t\t\t\t\tModule(\"Child\", tt.provide(0)),\n\t\t\t\t\tDecorate(func(a int) int { return a + 5 }),\n\t\t\t\t\tInvoke(func(a int) {\n\t\t\t\t\t\tinvoked = true\n\t\t\t\t\t}),\n\t\t\t\t)\n\t\t\t\terr := app.Err()\n\t\t\t\tif tt.private {\n\t\t\t\t\trequire.Error(t, err)\n\t\t\t\t\tassert.Contains(t, err.Error(), \"missing dependencies for function\")\n\t\t\t\t\tassert.Contains(t, err.Error(), \"missing type: int\")\n\t\t\t\t} else {\n\t\t\t\t\trequire.NoError(t, err)\n\t\t\t\t\tassert.True(t, invoked)\n\t\t\t\t}\n\t\t\t})\n\t\t})\n\t}\n}\n\nfunc TestWithLoggerErrorUseDefault(t *testing.T) {\n\t// This test cannot be run in paralllel with the others because\n\t// it hijacks stderr.\n\n\t// Temporarily hijack stderr and restore it after this test so\n\t// that we can assert its contents.\n\tf, err := os.CreateTemp(t.TempDir(), \"stderr\")\n\tif err != nil {\n\t\tt.Fatalf(\"could not open a file for writing\")\n\t}\n\tdefer func(oldStderr *os.File) {\n\t\tassert.NoError(t, f.Close())\n\t\tos.Stderr = oldStderr\n\t}(os.Stderr)\n\tos.Stderr = f\n\n\tapp := New(\n\t\tSupply(zap.NewNop()),\n\t\tWithLogger(&bytes.Buffer{}),\n\t)\n\terr = app.Err()\n\trequire.Error(t, err)\n\tassert.Contains(t,\n\t\terr.Error(),\n\t\t\"must provide constructor function, got  (type *bytes.Buffer)\",\n\t)\n\n\tstderr, err := os.ReadFile(f.Name())\n\trequire.NoError(t, err)\n\n\t// Example output:\n\t// [Fx] SUPPLY  *zap.Logger\n\t// [Fx] ERROR   Failed to initialize custom logger: fx.WithLogger() from:\n\t// go.uber.org/fx_test.TestSetupLogger.func3\n\t//        /Users/abg/dev/fx/app_test.go:334\n\t// testing.tRunner\n\t//        /usr/local/Cellar/go/1.16.4/libexec/src/testing/testing.go:1193\n\t// Failed: must provide constructor function, got  (type *bytes.Buffer)\n\n\tout := string(stderr)\n\tassert.Contains(t, out, \"[Fx] SUPPLY\\t*zap.Logger\\n\")\n\tassert.Contains(t, out, \"[Fx] ERROR\\t\\tFailed to initialize custom logger: fx.WithLogger\")\n\tassert.Contains(t, out, \"must provide constructor function, got  (type *bytes.Buffer)\\n\")\n}\n\nfunc TestWithLogger(t *testing.T) {\n\tt.Parallel()\n\n\tt.Run(\"initializing custom logger\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tvar spy fxlog.Spy\n\t\tapp := fxtest.New(t,\n\t\t\tSupply(&spy),\n\t\t\tWithLogger(func(spy *fxlog.Spy) fxevent.Logger {\n\t\t\t\treturn spy\n\t\t\t}),\n\t\t)\n\n\t\tassert.Equal(t, []string{\n\t\t\t\"Provided\", \"Provided\", \"Provided\", \"Supplied\", \"BeforeRun\", \"Run\", \"LoggerInitialized\",\n\t\t}, spy.EventTypes())\n\n\t\tspy.Reset()\n\t\tapp.RequireStart().RequireStop()\n\n\t\trequire.NoError(t, app.Err())\n\n\t\tassert.Equal(t, []string{\"Started\", \"Stopped\"}, spy.EventTypes())\n\t})\n\n\tt.Run(\"error in Provide shows logs\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tvar spy fxlog.Spy\n\t\tapp := New(\n\t\t\tSupply(&spy),\n\t\t\tWithLogger(func(spy *fxlog.Spy) fxevent.Logger {\n\t\t\t\treturn spy\n\t\t\t}),\n\t\t\tProvide(&bytes.Buffer{}), // not passing in a constructor.\n\t\t)\n\n\t\terr := app.Err()\n\t\trequire.Error(t, err)\n\t\tassert.Contains(t,\n\t\t\terr.Error(),\n\t\t\t\"must provide constructor function, got  (type *bytes.Buffer)\",\n\t\t)\n\n\t\tassert.Equal(t, []string{\"Provided\", \"Provided\", \"Provided\", \"Supplied\", \"Provided\", \"BeforeRun\", \"Run\", \"LoggerInitialized\"}, spy.EventTypes())\n\t})\n\n\tt.Run(\"logger failed to build\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tvar buff bytes.Buffer\n\t\tapp := New(\n\t\t\tLogger(log.New(&buff, \"\", 0)),\n\t\t\tWithLogger(func() (fxevent.Logger, error) {\n\t\t\t\treturn nil, errors.New(\"great sadness\")\n\t\t\t}),\n\t\t)\n\n\t\terr := app.Err()\n\t\trequire.Error(t, err)\n\t\tassert.Contains(t, err.Error(), \"great sadness\")\n\n\t\tout := buff.String()\n\t\tassert.Contains(t, out, \"[Fx] ERROR\\t\\tFailed to initialize custom logger\")\n\t})\n\n\tt.Run(\"logger dependency failed to build\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tvar buff bytes.Buffer\n\t\tapp := New(\n\t\t\tLogger(log.New(&buff, \"\", 0)),\n\t\t\tProvide(func() (*zap.Logger, error) {\n\t\t\t\treturn nil, errors.New(\"great sadness\")\n\t\t\t}),\n\t\t\tWithLogger(func(log *zap.Logger) fxevent.Logger {\n\t\t\t\tt.Errorf(\"WithLogger must not be called\")\n\t\t\t\tpanic(\"must not be called\")\n\t\t\t}),\n\t\t)\n\n\t\terr := app.Err()\n\t\trequire.Error(t, err)\n\t\tassert.Contains(t, err.Error(), \"great sadness\")\n\n\t\tout := buff.String()\n\t\tassert.Contains(t, out, \"[Fx] PROVIDE\\t*zap.Logger\")\n\t\tassert.Contains(t, out, \"[Fx] ERROR\\t\\tFailed to initialize custom logger\")\n\t})\n}\n\nfunc getInt() int { return 0 }\n\nfunc decorateInt(i int) int { return i }\n\nvar moduleA = Module(\n\t\"ModuleA\",\n\tProvide(getInt),\n\tDecorate(decorateInt),\n\tSupply(int64(14)),\n\tReplace(\"foo\"),\n)\n\nfunc getModuleB() Option {\n\treturn Module(\n\t\t\"ModuleB\",\n\t\tmoduleA,\n\t)\n}\n\nfunc TestModuleTrace(t *testing.T) {\n\tt.Parallel()\n\n\tmoduleC := Module(\n\t\t\"ModuleC\",\n\t\tgetModuleB(),\n\t)\n\n\tapp, spy := NewSpied(moduleC)\n\trequire.NoError(t, app.Err())\n\n\twantTrace, err := regexp.Compile(\n\t\t// Provide/decorate itself, initialized via init.\n\t\t\"^go.uber.org/fx_test.init \\\\(.*fx/app_test.go:.*\\\\)\\n\" +\n\t\t\t// ModuleA initialized via init.\n\t\t\t\"go.uber.org/fx_test.init \\\\(.*fx/app_test.go:.*\\\\) \\\\(ModuleA\\\\)\\n\" +\n\t\t\t// ModuleB from getModuleB.\n\t\t\t\"go.uber.org/fx_test.getModuleB \\\\(.*fx/app_test.go:.*\\\\) \\\\(ModuleB\\\\)\\n\" +\n\t\t\t// ModuleC above.\n\t\t\t\"go.uber.org/fx_test.TestModuleTrace \\\\(.*fx/app_test.go:.*\\\\) \\\\(ModuleC\\\\)\\n\" +\n\t\t\t// Top-level app & corresponding module created by NewSpied.\n\t\t\t\"go.uber.org/fx_test.NewSpied \\\\(.*fx/app_test.go:.*\\\\)$\",\n\t)\n\trequire.NoError(t, err, \"test regexp compilation error\")\n\n\tfor _, tt := range []struct {\n\t\tdesc     string\n\t\tgetTrace func(t *testing.T) []string\n\t}{\n\t\t{\n\t\t\tdesc: \"Provide\",\n\t\t\tgetTrace: func(t *testing.T) []string {\n\t\t\t\tt.Helper()\n\t\t\t\tvar event *fxevent.Provided\n\t\t\t\tfor _, e := range spy.Events().SelectByTypeName(\"Provided\") {\n\t\t\t\t\tpe, ok := e.(*fxevent.Provided)\n\t\t\t\t\tif !ok {\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\n\t\t\t\t\tif strings.HasSuffix(pe.ConstructorName, \"getInt()\") {\n\t\t\t\t\t\tevent = pe\n\t\t\t\t\t\tbreak\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\trequire.NotNil(t, event, \"could not find provide event for getInt()\")\n\t\t\t\treturn event.ModuleTrace\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tdesc: \"Decorate\",\n\t\t\tgetTrace: func(t *testing.T) []string {\n\t\t\t\tt.Helper()\n\t\t\t\tevents := spy.Events().SelectByTypeName(\"Decorated\")\n\t\t\t\trequire.Len(t, events, 1)\n\t\t\t\tevent, ok := events[0].(*fxevent.Decorated)\n\t\t\t\trequire.True(t, ok)\n\t\t\t\treturn event.ModuleTrace\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tdesc: \"Supply\",\n\t\t\tgetTrace: func(t *testing.T) []string {\n\t\t\t\tt.Helper()\n\t\t\t\tevents := spy.Events().SelectByTypeName(\"Supplied\")\n\t\t\t\trequire.Len(t, events, 1)\n\t\t\t\tevent, ok := events[0].(*fxevent.Supplied)\n\t\t\t\trequire.True(t, ok)\n\t\t\t\treturn event.ModuleTrace\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tdesc: \"Replaced\",\n\t\t\tgetTrace: func(t *testing.T) []string {\n\t\t\t\tt.Helper()\n\t\t\t\tevents := spy.Events().SelectByTypeName(\"Replaced\")\n\t\t\t\trequire.Len(t, events, 1)\n\t\t\t\tevent, ok := events[0].(*fxevent.Replaced)\n\t\t\t\trequire.True(t, ok)\n\t\t\t\treturn event.ModuleTrace\n\t\t\t},\n\t\t},\n\t} {\n\t\tt.Run(tt.desc, func(t *testing.T) {\n\t\t\tgotTrace := strings.Join(tt.getTrace(t), \"\\n\")\n\t\t\tassert.Regexp(t, wantTrace, gotTrace)\n\t\t})\n\t}\n}\n\nfunc TestRunEventEmission(t *testing.T) {\n\tt.Parallel()\n\n\tfor _, tt := range []struct {\n\t\tdesc          string\n\t\tgiveOpts      []Option\n\t\twantRunEvents []fxevent.Run\n\t\twantErr       string\n\t}{\n\t\t{\n\t\t\tdesc: \"Simple Provide And Decorate\",\n\t\t\tgiveOpts: []Option{\n\t\t\t\tProvide(func() int { return 5 }),\n\t\t\t\tDecorate(func(int) int { return 6 }),\n\t\t\t\tInvoke(func(int) {}),\n\t\t\t},\n\t\t\twantRunEvents: []fxevent.Run{\n\t\t\t\t{\n\t\t\t\t\tName: \"go.uber.org/fx_test.TestRunEventEmission.func1()\",\n\t\t\t\t\tKind: \"provide\",\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tName: \"go.uber.org/fx_test.TestRunEventEmission.func2()\",\n\t\t\t\t\tKind: \"decorate\",\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tdesc: \"Supply and Decorator Error\",\n\t\t\tgiveOpts: []Option{\n\t\t\t\tSupply(5),\n\t\t\t\tDecorate(func(int) (int, error) {\n\t\t\t\t\treturn 0, errors.New(\"humongous despair\")\n\t\t\t\t}),\n\t\t\t\tInvoke(func(int) {}),\n\t\t\t},\n\t\t\twantRunEvents: []fxevent.Run{\n\t\t\t\t{\n\t\t\t\t\tName: \"stub(int)\",\n\t\t\t\t\tKind: \"supply\",\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tName: \"go.uber.org/fx_test.TestRunEventEmission.func4()\",\n\t\t\t\t\tKind: \"decorate\",\n\t\t\t\t},\n\t\t\t},\n\t\t\twantErr: \"humongous despair\",\n\t\t},\n\t\t{\n\t\t\tdesc: \"Replace\",\n\t\t\tgiveOpts: []Option{\n\t\t\t\tProvide(func() int { return 5 }),\n\t\t\t\tReplace(6),\n\t\t\t\tInvoke(func(int) {}),\n\t\t\t},\n\t\t\twantRunEvents: []fxevent.Run{\n\t\t\t\t{\n\t\t\t\t\tName: \"stub(int)\",\n\t\t\t\t\tKind: \"replace\",\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tdesc: \"Provide Error\",\n\t\t\tgiveOpts: []Option{\n\t\t\t\tProvide(func() (int, error) {\n\t\t\t\t\treturn 0, errors.New(\"terrible sadness\")\n\t\t\t\t}),\n\t\t\t\tInvoke(func(int) {}),\n\t\t\t},\n\t\t\twantRunEvents: []fxevent.Run{\n\t\t\t\t{\n\t\t\t\t\tName: \"go.uber.org/fx_test.TestRunEventEmission.func8()\",\n\t\t\t\t\tKind: \"provide\",\n\t\t\t\t},\n\t\t\t},\n\t\t\twantErr: \"terrible sadness\",\n\t\t},\n\t\t{\n\t\t\tdesc: \"Provide Panic\",\n\t\t\tgiveOpts: []Option{\n\t\t\t\tProvide(func() int {\n\t\t\t\t\tpanic(\"bad provide\")\n\t\t\t\t}),\n\t\t\t\tRecoverFromPanics(),\n\t\t\t\tInvoke(func(int) {}),\n\t\t\t},\n\t\t\twantRunEvents: []fxevent.Run{\n\t\t\t\t{\n\t\t\t\t\tName: \"go.uber.org/fx_test.TestRunEventEmission.func10()\",\n\t\t\t\t\tKind: \"provide\",\n\t\t\t\t},\n\t\t\t},\n\t\t\twantErr: `panic: \"bad provide\"`,\n\t\t},\n\t\t{\n\t\t\tdesc: \"Decorate Panic\",\n\t\t\tgiveOpts: []Option{\n\t\t\t\tSupply(5),\n\t\t\t\tDecorate(func(int) int {\n\t\t\t\t\tpanic(\"bad decorate\")\n\t\t\t\t}),\n\t\t\t\tRecoverFromPanics(),\n\t\t\t\tInvoke(func(int) {}),\n\t\t\t},\n\t\t\twantRunEvents: []fxevent.Run{\n\t\t\t\t{\n\t\t\t\t\tName: \"stub(int)\",\n\t\t\t\t\tKind: \"supply\",\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tName: \"go.uber.org/fx_test.TestRunEventEmission.func12()\",\n\t\t\t\t\tKind: \"decorate\",\n\t\t\t\t},\n\t\t\t},\n\t\t\twantErr: `panic: \"bad decorate\"`,\n\t\t},\n\t} {\n\t\tt.Run(tt.desc, func(t *testing.T) {\n\t\t\tt.Parallel()\n\n\t\t\tapp, spy := NewSpied(tt.giveOpts...)\n\t\t\tif tt.wantErr != \"\" {\n\t\t\t\tassert.ErrorContains(t, app.Err(), tt.wantErr)\n\t\t\t} else {\n\t\t\t\tassert.NoError(t, app.Err())\n\t\t\t}\n\n\t\t\tgotBeforeEvents := spy.Events().SelectByTypeName(\"BeforeRun\")\n\t\t\tgotEvents := spy.Events().SelectByTypeName(\"Run\")\n\t\t\trequire.Len(t, gotBeforeEvents, len(tt.wantRunEvents), \"wrong number of before-run events\")\n\t\t\trequire.Len(t, gotEvents, len(tt.wantRunEvents), \"wrong number of run events\")\n\t\t\t// BeforeRun events are just a reduced-field version of Run events\n\t\t\tfor i, event := range gotBeforeEvents {\n\t\t\t\trEvent, ok := event.(*fxevent.BeforeRun)\n\t\t\t\trequire.True(t, ok)\n\n\t\t\t\tassert.Equal(t, tt.wantRunEvents[i].Name, rEvent.Name)\n\t\t\t\tassert.Equal(t, tt.wantRunEvents[i].Kind, rEvent.Kind)\n\t\t\t}\n\t\t\tfor i, event := range gotEvents {\n\t\t\t\trEvent, ok := event.(*fxevent.Run)\n\t\t\t\trequire.True(t, ok)\n\n\t\t\t\tassert.Equal(t, tt.wantRunEvents[i].Name, rEvent.Name)\n\t\t\t\tassert.Equal(t, tt.wantRunEvents[i].Kind, rEvent.Kind)\n\t\t\t\tif tt.wantErr != \"\" && i == len(gotEvents)-1 {\n\t\t\t\t\tassert.ErrorContains(t, rEvent.Err, tt.wantErr)\n\t\t\t\t} else {\n\t\t\t\t\tassert.NoError(t, rEvent.Err)\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t}\n}\n\ntype customError struct {\n\terr error\n}\n\nfunc (e *customError) Error() string {\n\treturn fmt.Sprintf(\"custom error: %v\", e.err)\n}\n\nfunc (e *customError) Unwrap() error {\n\treturn e.err\n}\n\ntype errHandlerFunc func(error)\n\nfunc (f errHandlerFunc) HandleError(err error) { f(err) }\n\nfunc TestInvokes(t *testing.T) {\n\tt.Parallel()\n\n\tt.Run(\"Success event\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tapp, spy := NewSpied(\n\t\t\tInvoke(func() {}),\n\t\t)\n\t\trequire.NoError(t, app.Err())\n\n\t\tinvoked := spy.Events().SelectByTypeName(\"Invoked\")\n\t\trequire.Len(t, invoked, 1)\n\t\tassert.NoError(t, invoked[0].(*fxevent.Invoked).Err)\n\t})\n\n\tt.Run(\"Failure event\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\twantErr := errors.New(\"great sadness\")\n\t\tapp, spy := NewSpied(\n\t\t\tInvoke(func() error {\n\t\t\t\treturn wantErr\n\t\t\t}),\n\t\t)\n\t\trequire.Error(t, app.Err())\n\n\t\tinvoked := spy.Events().SelectByTypeName(\"Invoked\")\n\t\trequire.Len(t, invoked, 1)\n\t\trequire.Error(t, invoked[0].(*fxevent.Invoked).Err)\n\t\trequire.ErrorIs(t, invoked[0].(*fxevent.Invoked).Err, wantErr)\n\t})\n\n\tt.Run(\"ErrorsAreNotOverriden\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\ttype A struct{}\n\t\ttype B struct{}\n\n\t\tapp := NewForTest(t,\n\t\t\tProvide(func() B { return B{} }), // B inserted into the graph\n\t\t\tInvoke(func(A) {}),               // failed A invoke\n\t\t\tInvoke(func(B) {}),               // successful B invoke\n\t\t)\n\t\terr := app.Err()\n\t\trequire.Error(t, err)\n\t\tassert.Contains(t, err.Error(), \"missing type: fx_test.A\")\n\t})\n\n\tt.Run(\"ErrorHooksAreCalled\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\ttype A struct{}\n\n\t\tcount := 0\n\t\th := errHandlerFunc(func(err error) {\n\t\t\tcount++\n\t\t})\n\t\tNewForTest(t,\n\t\t\tInvoke(func(A) {}),\n\t\t\tErrorHook(h),\n\t\t)\n\t\tassert.Equal(t, 1, count)\n\t})\n\n\tt.Run(\"ErrorsAreWrapped\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\twantErr := errors.New(\"err\")\n\n\t\tapp := NewForTest(t,\n\t\t\tInvoke(func() error {\n\t\t\t\treturn &customError{err: wantErr}\n\t\t\t}),\n\t\t)\n\n\t\terr := app.Err()\n\t\trequire.Error(t, err)\n\t\tassert.ErrorIs(t, err, wantErr)\n\t\tvar ce *customError\n\t\tassert.ErrorAs(t, err, &ce)\n\t})\n}\n\nfunc TestError(t *testing.T) {\n\tt.Parallel()\n\n\tt.Run(\"NilErrorOption\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tvar invoked bool\n\n\t\tapp := NewForTest(t,\n\t\t\tError(nil),\n\t\t\tInvoke(func() { invoked = true }),\n\t\t)\n\t\terr := app.Err()\n\t\trequire.NoError(t, err)\n\t\tassert.True(t, invoked)\n\t})\n\n\tt.Run(\"SingleErrorOption\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\twantErr := errors.New(\"module failure\")\n\t\tapp := NewForTest(t,\n\t\t\tError(wantErr),\n\t\t\tInvoke(func() { t.Errorf(\"Invoke should not be called\") }),\n\t\t)\n\t\terr := app.Err()\n\t\trequire.Error(t, err)\n\t\tassert.EqualError(t, err, \"module failure\")\n\t\tassert.ErrorIs(t, err, wantErr)\n\t})\n\n\tt.Run(\"MultipleErrorOption\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\ttype A struct{}\n\n\t\terrA := errors.New(\"module A failure\")\n\t\terrB := errors.New(\"module B failure\")\n\t\tapp := NewForTest(t,\n\t\t\tProvide(func() A {\n\t\t\t\tt.Errorf(\"Provide should not be called\")\n\t\t\t\treturn A{}\n\t\t\t},\n\t\t\t),\n\t\t\tInvoke(func(A) { t.Errorf(\"Invoke should not be called\") }),\n\t\t\tError(\n\t\t\t\terrA,\n\t\t\t\terrB,\n\t\t\t),\n\t\t)\n\t\terr := app.Err()\n\t\trequire.Error(t, err)\n\t\tassert.ErrorIs(t, err, errA)\n\t\tassert.ErrorIs(t, err, errB)\n\t\tassert.NotContains(t, err.Error(), \"not in the container\")\n\t})\n\n\tt.Run(\"ProvideAndInvokeErrorsAreIgnored\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\ttype A struct{}\n\t\ttype B struct{}\n\n\t\tapp := NewForTest(t,\n\t\t\tProvide(func(b B) A {\n\t\t\t\tt.Errorf(\"B is missing from the container; Provide should not be called\")\n\t\t\t\treturn A{}\n\t\t\t},\n\t\t\t),\n\t\t\tError(errors.New(\"module failure\")),\n\t\t\tInvoke(func(A) { t.Errorf(\"A was not provided; Invoke should not be called\") }),\n\t\t)\n\t\terr := app.Err()\n\t\tassert.EqualError(t, err, \"module failure\")\n\t})\n}\n\nfunc TestOptions(t *testing.T) {\n\tt.Parallel()\n\n\tt.Run(\"OptionsComposition\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tvar n int\n\t\tconstruct := func() struct{} {\n\t\t\tn++\n\t\t\treturn struct{}{}\n\t\t}\n\t\tuse := func(struct{}) {\n\t\t\tn++\n\t\t}\n\t\tapp := fxtest.New(t, Options(Provide(construct), Invoke(use)))\n\t\tdefer app.RequireStart().RequireStop()\n\t\tassert.Equal(t, 2, n)\n\t})\n\n\tt.Run(\"ProvidesCalledInGraphOrder\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\ttype type1 struct{}\n\t\ttype type2 struct{}\n\t\ttype type3 struct{}\n\n\t\tinitOrder := 0\n\t\tnew1 := func() type1 {\n\t\t\tinitOrder++\n\t\t\tassert.Equal(t, 1, initOrder)\n\t\t\treturn type1{}\n\t\t}\n\t\tnew2 := func(type1) type2 {\n\t\t\tinitOrder++\n\t\t\tassert.Equal(t, 2, initOrder)\n\t\t\treturn type2{}\n\t\t}\n\t\tnew3 := func(type1, type2) type3 {\n\t\t\tinitOrder++\n\t\t\tassert.Equal(t, 3, initOrder)\n\t\t\treturn type3{}\n\t\t}\n\t\tbiz := func(s1 type1, s2 type2, s3 type3) {\n\t\t\tinitOrder++\n\t\t\tassert.Equal(t, 4, initOrder)\n\t\t}\n\t\tapp := fxtest.New(t,\n\t\t\tProvide(new1, new2, new3),\n\t\t\tInvoke(biz),\n\t\t)\n\t\tdefer app.RequireStart().RequireStop()\n\t\tassert.Equal(t, 4, initOrder)\n\t})\n\n\tt.Run(\"ProvidesCalledLazily\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tcount := 0\n\t\tnewBuffer := func() *bytes.Buffer {\n\t\t\tt.Error(\"this module should not init: no provided type relies on it\")\n\t\t\treturn nil\n\t\t}\n\t\tnewEmpty := func() struct{} {\n\t\t\tcount++\n\t\t\treturn struct{}{}\n\t\t}\n\t\tapp := fxtest.New(t,\n\t\t\tProvide(newBuffer, newEmpty),\n\t\t\tInvoke(func(struct{}) { count++ }),\n\t\t)\n\t\tdefer app.RequireStart().RequireStop()\n\t\tassert.Equal(t, 2, count)\n\t})\n\n\tt.Run(\"Error\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tspy := new(fxlog.Spy)\n\t\tNew(\n\t\t\tProvide(&bytes.Buffer{}), // error, not a constructor\n\t\t\tWithLogger(func() fxevent.Logger { return spy }),\n\t\t)\n\t\trequire.Equal(t, []string{\"Provided\", \"Provided\", \"Provided\", \"Provided\", \"LoggerInitialized\"}, spy.EventTypes())\n\t\t// First 3 provides are Fx types (Lifecycle, Shutdowner, DotGraph).\n\t\tassert.Contains(t, spy.Events()[3].(*fxevent.Provided).Err.Error(), \"must provide constructor function\")\n\t})\n}\n\nfunc TestTimeoutOptions(t *testing.T) {\n\tt.Parallel()\n\n\tconst timeout = time.Minute\n\t// Further assertions can't succeed unless the test timeout is greater than the default.\n\trequire.True(t, timeout > DefaultTimeout, \"test assertions require timeout greater than default\")\n\n\tvar started, stopped bool\n\tassertCustomContext := func(ctx context.Context, phase string) {\n\t\tdeadline, ok := ctx.Deadline()\n\t\tif assert.True(t, ok, \"no %s deadline\", phase) {\n\t\t\tremaining := time.Until(deadline)\n\t\t\tassert.True(t, remaining > DefaultTimeout, \"didn't respect customized %s timeout\", phase)\n\t\t}\n\t}\n\tverify := func(lc Lifecycle) {\n\t\tlc.Append(Hook{\n\t\t\tOnStart: func(ctx context.Context) error {\n\t\t\t\tassertCustomContext(ctx, \"start\")\n\t\t\t\tstarted = true\n\t\t\t\treturn nil\n\t\t\t},\n\t\t\tOnStop: func(ctx context.Context) error {\n\t\t\t\tassertCustomContext(ctx, \"stop\")\n\t\t\t\tstopped = true\n\t\t\t\treturn nil\n\t\t\t},\n\t\t})\n\t}\n\tapp := fxtest.New(\n\t\tt,\n\t\tInvoke(verify),\n\t\tStartTimeout(timeout),\n\t\tStopTimeout(timeout),\n\t)\n\n\tapp.RequireStart().RequireStop()\n\tassert.True(t, started, \"app wasn't started\")\n\tassert.True(t, stopped, \"app wasn't stopped\")\n}\n\nfunc TestAppRunTimeout(t *testing.T) {\n\tt.Parallel()\n\n\t// Fails with an error immediately.\n\tfailure := func() error {\n\t\treturn errors.New(\"great sadness\")\n\t}\n\n\t// Builds a hook that takes much longer than the application timeout.\n\ttakeVeryLong := func(clock *fxclock.Mock) func() error {\n\t\treturn func() error {\n\t\t\t// We'll exceed the start and stop timeouts,\n\t\t\t// and then some.\n\t\t\tfor range 3 {\n\t\t\t\tclock.Add(time.Second)\n\t\t\t}\n\n\t\t\treturn errors.New(\"user should not see this\")\n\t\t}\n\t}\n\n\ttests := []struct {\n\t\tdesc string\n\n\t\t// buildHook builds and returns the hooks for this test case.\n\t\tbuildHooks func(*fxclock.Mock) []Hook\n\n\t\t// Type of the fxevent we want.\n\t\t// Does not reflect the exact value.\n\t\twantEventType fxevent.Event\n\t}{\n\t\t{\n\t\t\t// Timeout starting an application.\n\t\t\tdesc: \"OnStart timeout\",\n\t\t\tbuildHooks: func(clock *fxclock.Mock) []Hook {\n\t\t\t\treturn []Hook{\n\t\t\t\t\tStartHook(takeVeryLong(clock)),\n\t\t\t\t}\n\t\t\t},\n\t\t\twantEventType: &fxevent.Started{},\n\t\t},\n\t\t{\n\t\t\t// Timeout during a rollback because start failed.\n\t\t\tdesc: \"rollback timeout\",\n\t\t\tbuildHooks: func(clock *fxclock.Mock) []Hook {\n\t\t\t\treturn []Hook{\n\t\t\t\t\t// The hooks are separate because\n\t\t\t\t\t// OnStop will not be run if that hook failed.\n\t\t\t\t\tStartHook(takeVeryLong(clock)),\n\t\t\t\t\tStopHook(failure),\n\t\t\t\t}\n\t\t\t},\n\t\t\twantEventType: &fxevent.Started{},\n\t\t},\n\t\t{\n\t\t\t// Timeout during a stop.\n\t\t\tdesc: \"OnStop timeout\",\n\t\t\tbuildHooks: func(clock *fxclock.Mock) []Hook {\n\t\t\t\treturn []Hook{\n\t\t\t\t\tStopHook(takeVeryLong(clock)),\n\t\t\t\t}\n\t\t\t},\n\t\t\twantEventType: &fxevent.Stopped{},\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.desc, func(t *testing.T) {\n\t\t\tt.Parallel()\n\n\t\t\tmockClock := fxclock.NewMock()\n\n\t\t\tvar (\n\t\t\t\texitCode int\n\t\t\t\texited   bool\n\t\t\t)\n\t\t\texit := func(code int) {\n\t\t\t\texited = true\n\t\t\t\texitCode = code\n\t\t\t}\n\t\t\tdefer func() {\n\t\t\t\tassert.True(t, exited,\n\t\t\t\t\t\"os.Exit must be called\")\n\t\t\t}()\n\n\t\t\t// If the OnStart hook for this is invoked,\n\t\t\t// it means that the Start did not fail.\n\t\t\t// In that case, shut down immediately\n\t\t\t// rather than block forever.\n\t\t\tshutdown := func(sd Shutdowner, lc Lifecycle) {\n\t\t\t\tlc.Append(Hook{\n\t\t\t\t\tOnStart: func(context.Context) error {\n\t\t\t\t\t\treturn sd.Shutdown()\n\t\t\t\t\t},\n\t\t\t\t})\n\t\t\t}\n\n\t\t\tapp, spy := NewSpied(\n\t\t\t\tStartTimeout(time.Second),\n\t\t\t\tStopTimeout(time.Second),\n\t\t\t\tWithExit(exit),\n\t\t\t\tWithClock(mockClock),\n\t\t\t\tInvoke(func(lc Lifecycle) {\n\t\t\t\t\thooks := tt.buildHooks(mockClock)\n\t\t\t\t\tfor _, h := range hooks {\n\t\t\t\t\t\tlc.Append(h)\n\t\t\t\t\t}\n\t\t\t\t}),\n\t\t\t\tInvoke(shutdown),\n\t\t\t)\n\n\t\t\tapp.Run()\n\t\t\tassert.NotZero(t, exitCode,\n\t\t\t\t\"exit code mismatch\")\n\n\t\t\teventType := reflect.TypeOf(tt.wantEventType).Elem().Name()\n\t\t\tmatchingEvents := spy.Events().SelectByTypeName(eventType)\n\t\t\trequire.Len(t, matchingEvents, 1,\n\t\t\t\t\"expected a %q event\", eventType)\n\n\t\t\tevent := matchingEvents[0]\n\t\t\terrv := reflect.ValueOf(event).Elem().FieldByName(\"Err\")\n\t\t\trequire.True(t, errv.IsValid(),\n\t\t\t\t\"event %q does not have an Err attribute\", eventType)\n\n\t\t\terr, _ := errv.Interface().(error)\n\t\t\tassert.ErrorIs(t, err, context.DeadlineExceeded,\n\t\t\t\t\"should fail because of a timeout\")\n\t\t})\n\t}\n}\n\nfunc TestAppStart(t *testing.T) {\n\tt.Parallel()\n\n\tt.Run(\"Timeout\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tmockClock := fxclock.NewMock()\n\n\t\ttype A struct{}\n\t\tblocker := func(lc Lifecycle) *A {\n\t\t\tlc.Append(\n\t\t\t\tHook{\n\t\t\t\t\tOnStart: func(ctx context.Context) error {\n\t\t\t\t\t\tmockClock.Add(5 * time.Second)\n\t\t\t\t\t\treturn ctx.Err()\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t)\n\t\t\treturn &A{}\n\t\t}\n\t\t// NOTE: for tests that gets cancelled/times out during lifecycle methods, it's possible\n\t\t// for them to run into race with fxevent logs getting written to testing.T with the\n\t\t// remainder of the tests. As a workaround, we provide fxlog.Spy to prevent the lifecycle\n\t\t// goroutine from writing to testing.T.\n\t\tspy := new(fxlog.Spy)\n\t\tapp := NewForTest(t,\n\t\t\tWithLogger(func() fxevent.Logger { return spy }),\n\t\t\tWithClock(mockClock),\n\t\t\tProvide(blocker),\n\t\t\tInvoke(func(*A) {}),\n\t\t)\n\n\t\tctx, cancel := mockClock.WithTimeout(context.Background(), time.Second)\n\n\t\terr := app.Start(ctx)\n\t\trequire.Error(t, err)\n\t\tassert.Contains(t, \"context deadline exceeded\", err.Error())\n\t\tcancel()\n\t})\n\n\tt.Run(\"TimeoutWithFinishedHooks\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tmockClock := fxclock.NewMock()\n\n\t\ttype A struct{}\n\t\ttype B struct{ A *A }\n\t\ttype C struct{ B *B }\n\t\tnewA := func(lc Lifecycle) *A {\n\t\t\tlc.Append(\n\t\t\t\tHook{\n\t\t\t\t\tOnStart: func(context.Context) error {\n\t\t\t\t\t\tmockClock.Add(100 * time.Millisecond)\n\t\t\t\t\t\treturn nil\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t)\n\t\t\treturn &A{}\n\t\t}\n\t\tnewB := func(lc Lifecycle, a *A) *B {\n\t\t\tlc.Append(\n\t\t\t\tHook{\n\t\t\t\t\tOnStart: func(context.Context) error {\n\t\t\t\t\t\tmockClock.Add(300 * time.Millisecond)\n\t\t\t\t\t\treturn nil\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t)\n\t\t\treturn &B{a}\n\t\t}\n\t\tnewC := func(lc Lifecycle, b *B) *C {\n\t\t\tlc.Append(\n\t\t\t\tHook{\n\t\t\t\t\tOnStart: func(ctx context.Context) error {\n\t\t\t\t\t\tmockClock.Add(5 * time.Second)\n\t\t\t\t\t\treturn ctx.Err()\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t)\n\t\t\treturn &C{b}\n\t\t}\n\n\t\t// NOTE: for tests that gets cancelled/times out during lifecycle methods, it's possible\n\t\t// for them to run into race with fxevent logs getting written to testing.T with the\n\t\t// remainder of the tests. As a workaround, we provide fxlog.Spy to prevent the lifecycle\n\t\t// goroutine from writing to testing.T.\n\t\tspy := new(fxlog.Spy)\n\t\tapp := New(\n\t\t\tWithLogger(func() fxevent.Logger { return spy }),\n\t\t\tWithClock(mockClock),\n\t\t\tProvide(newA, newB, newC),\n\t\t\tInvoke(func(*C) {}),\n\t\t)\n\n\t\tctx, cancel := mockClock.WithTimeout(context.Background(), time.Second)\n\t\tdefer cancel()\n\n\t\terr := app.Start(ctx)\n\t\trequire.Error(t, err)\n\t\tassert.Contains(t, \"context deadline exceeded\", err.Error())\n\t})\n\n\tt.Run(\"CtxCancelledDuringStart\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\ttype A struct{}\n\t\trunning := make(chan struct{})\n\t\tnewA := func(lc Lifecycle) *A {\n\t\t\tlc.Append(\n\t\t\t\tHook{\n\t\t\t\t\tOnStart: func(ctx context.Context) error {\n\t\t\t\t\t\tclose(running)\n\t\t\t\t\t\t<-ctx.Done()\n\t\t\t\t\t\treturn ctx.Err()\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t)\n\t\t\treturn &A{}\n\t\t}\n\n\t\t// NOTE: for tests that gets cancelled/times out during lifecycle methods, it's possible\n\t\t// for them to run into race with fxevent logs getting written to testing.T with the\n\t\t// remainder of the tests. As a workaround, we provide fxlog.Spy to prevent the lifecycle\n\t\t// goroutine from writing to testing.T.\n\t\tspy := new(fxlog.Spy)\n\t\tapp := New(\n\t\t\tWithLogger(func() fxevent.Logger { return spy }),\n\t\t\tProvide(newA),\n\t\t\tInvoke(func(*A) {}),\n\t\t)\n\n\t\tctx, cancel := context.WithCancel(context.Background())\n\t\tgo func() {\n\t\t\t<-running\n\t\t\tcancel()\n\t\t}()\n\t\terr := app.Start(ctx)\n\t\trequire.Error(t, err)\n\t\tassert.NotContains(t, err.Error(), \"context deadline exceeded\")\n\t\tassert.NotContains(t, err.Error(), \"timed out while executing hook OnStart\")\n\t})\n\n\tt.Run(\"race test\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tsecondStart := make(chan struct{}, 1)\n\t\tfirstStop := make(chan struct{}, 1)\n\t\tstartReturn := make(chan struct{}, 1)\n\n\t\tvar stop1Run bool\n\t\tapp := New(\n\t\t\tInvoke(func(lc Lifecycle) {\n\t\t\t\tlc.Append(Hook{\n\t\t\t\t\tOnStart: func(context.Context) error {\n\t\t\t\t\t\treturn nil\n\t\t\t\t\t},\n\t\t\t\t\tOnStop: func(context.Context) error {\n\t\t\t\t\t\tif stop1Run {\n\t\t\t\t\t\t\trequire.Fail(t, \"Hooks should only run once\")\n\t\t\t\t\t\t}\n\t\t\t\t\t\tstop1Run = true\n\t\t\t\t\t\tclose(firstStop)\n\t\t\t\t\t\t<-startReturn\n\t\t\t\t\t\treturn nil\n\t\t\t\t\t},\n\t\t\t\t})\n\t\t\t\tlc.Append(Hook{\n\t\t\t\t\tOnStart: func(context.Context) error {\n\t\t\t\t\t\tclose(secondStart)\n\t\t\t\t\t\t<-firstStop\n\t\t\t\t\t\treturn nil\n\t\t\t\t\t},\n\t\t\t\t\tOnStop: func(context.Context) error {\n\t\t\t\t\t\tassert.Fail(t, \"Stop hook 2 should not be called \"+\n\t\t\t\t\t\t\t\"if start hook 2 does not finish\")\n\t\t\t\t\t\treturn nil\n\t\t\t\t\t},\n\t\t\t\t})\n\t\t\t}),\n\t\t)\n\n\t\tgo func() {\n\t\t\tassert.NoError(t, app.Start(context.Background()))\n\t\t\tclose(startReturn)\n\t\t}()\n\n\t\t<-secondStart\n\t\tassert.NoError(t, app.Stop(context.Background()))\n\t\tassert.True(t, stop1Run)\n\t})\n\n\tt.Run(\"CtxTimeoutDuringStartStillRunsStopHooks\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tvar ran bool\n\t\tmockClock := fxclock.NewMock()\n\t\tapp := New(\n\t\t\tWithClock(mockClock),\n\t\t\tInvoke(func(lc Lifecycle) {\n\t\t\t\tlc.Append(Hook{\n\t\t\t\t\tOnStart: func(ctx context.Context) error {\n\t\t\t\t\t\treturn nil\n\t\t\t\t\t},\n\t\t\t\t\tOnStop: func(ctx context.Context) error {\n\t\t\t\t\t\tran = true\n\t\t\t\t\t\treturn nil\n\t\t\t\t\t},\n\t\t\t\t})\n\t\t\t\tlc.Append(Hook{\n\t\t\t\t\tOnStart: func(ctx context.Context) error {\n\t\t\t\t\t\tmockClock.Add(5 * time.Second)\n\t\t\t\t\t\treturn ctx.Err()\n\t\t\t\t\t},\n\t\t\t\t\tOnStop: func(ctx context.Context) error {\n\t\t\t\t\t\tassert.Fail(t, \"This Stop hook should not be called\")\n\t\t\t\t\t\treturn nil\n\t\t\t\t\t},\n\t\t\t\t})\n\t\t\t}),\n\t\t)\n\n\t\tstartCtx, cancelStart := mockClock.WithTimeout(context.Background(), time.Second)\n\t\tdefer cancelStart()\n\t\terr := app.Start(startCtx)\n\t\trequire.Error(t, err)\n\t\tassert.ErrorContains(t, err, \"context deadline exceeded\")\n\t\trequire.NoError(t, app.Stop(context.Background()))\n\t\tassert.True(t, ran, \"Stop hook for the Start hook that finished running should have been called\")\n\t})\n\n\tt.Run(\"Rollback\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tfailStart := func(lc Lifecycle) struct{} {\n\t\t\tlc.Append(Hook{OnStart: func(context.Context) error {\n\t\t\t\treturn errors.New(\"OnStart fail\")\n\t\t\t}})\n\t\t\treturn struct{}{}\n\t\t}\n\t\tapp, spy := NewSpied(\n\t\t\tProvide(failStart),\n\t\t\tInvoke(func(struct{}) {}),\n\t\t)\n\t\terr := app.Start(context.Background())\n\t\trequire.Error(t, err)\n\t\tassert.Contains(t, err.Error(), \"OnStart fail\")\n\n\t\tassert.Equal(t, []string{\n\t\t\t\"Provided\", \"Provided\", \"Provided\", \"Provided\",\n\t\t\t\"LoggerInitialized\",\n\t\t\t\"Invoking\",\n\t\t\t\"BeforeRun\",\n\t\t\t\"Run\",\n\t\t\t\"BeforeRun\",\n\t\t\t\"Run\",\n\t\t\t\"Invoked\",\n\t\t\t\"OnStartExecuting\", \"OnStartExecuted\",\n\t\t\t\"RollingBack\",\n\t\t\t\"RolledBack\",\n\t\t\t\"Started\",\n\t\t}, spy.EventTypes())\n\t})\n\n\tt.Run(\"StartAndStopErrors\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\terrStop1 := errors.New(\"OnStop fail 1\")\n\t\terrStart2 := errors.New(\"OnStart fail 2\")\n\t\tfail := func(lc Lifecycle) struct{} {\n\t\t\tlc.Append(Hook{\n\t\t\t\tOnStart: func(context.Context) error { return nil },\n\t\t\t\tOnStop:  func(context.Context) error { return errStop1 },\n\t\t\t})\n\t\t\tlc.Append(Hook{\n\t\t\t\tOnStart: func(context.Context) error { return errStart2 },\n\t\t\t\tOnStop:  func(context.Context) error { assert.Fail(t, \"should be never called\"); return nil },\n\t\t\t})\n\t\t\treturn struct{}{}\n\t\t}\n\t\tapp, spy := NewSpied(\n\t\t\tProvide(fail),\n\t\t\tInvoke(func(struct{}) {}),\n\t\t)\n\t\terr := app.Start(context.Background())\n\t\trequire.Error(t, err)\n\t\tassert.Equal(t, []error{errStart2, errStop1}, multierr.Errors(err))\n\n\t\tassert.Equal(t, []string{\n\t\t\t\"Provided\", \"Provided\", \"Provided\", \"Provided\",\n\t\t\t\"LoggerInitialized\",\n\t\t\t\"Invoking\",\n\t\t\t\"BeforeRun\",\n\t\t\t\"Run\",\n\t\t\t\"BeforeRun\",\n\t\t\t\"Run\",\n\t\t\t\"Invoked\",\n\t\t\t\"OnStartExecuting\", \"OnStartExecuted\",\n\t\t\t\"OnStartExecuting\", \"OnStartExecuted\",\n\t\t\t\"RollingBack\",\n\t\t\t\"OnStopExecuting\", \"OnStopExecuted\",\n\t\t\t\"RolledBack\",\n\t\t\t\"Started\",\n\t\t}, spy.EventTypes())\n\t})\n\n\tt.Run(\"InvokeNonFunction\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tspy := new(fxlog.Spy)\n\n\t\tapp := New(WithLogger(func() fxevent.Logger { return spy }), Invoke(struct{}{}))\n\t\terr := app.Err()\n\t\trequire.Error(t, err, \"expected start failure\")\n\t\tassert.Contains(t, err.Error(), \"can't invoke non-function\")\n\n\t\t// Example\n\t\t// fx.Invoke({}) called from:\n\t\t// go.uber.org/fx_test.TestAppStart.func4\n\t\t//         /.../fx/app_test.go:525\n\t\t// testing.tRunner\n\t\t//         /.../go/1.13.3/libexec/src/testing/testing.go:909\n\t\t// Failed: can't invoke non-function {} (type struct {})\n\t\trequire.Equal(t,\n\t\t\t[]string{\"Provided\", \"Provided\", \"Provided\", \"LoggerInitialized\", \"Invoking\", \"Invoked\"},\n\t\t\tspy.EventTypes())\n\t\tfailedEvent := spy.Events()[len(spy.EventTypes())-1].(*fxevent.Invoked)\n\t\tassert.Contains(t, failedEvent.Err.Error(), \"can't invoke non-function\")\n\t\tassert.Contains(t, failedEvent.Trace, \"go.uber.org/fx_test.TestAppStart\")\n\t\tassert.Contains(t, failedEvent.Trace, \"/app_test.go\")\n\t})\n\n\tt.Run(\"ProvidingAProvideShouldFail\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\ttype type1 struct{}\n\t\ttype type2 struct{}\n\t\ttype type3 struct{}\n\n\t\tapp := NewForTest(t,\n\t\t\tProvide(\n\t\t\t\tfunc() type1 { return type1{} },\n\t\t\t\tProvide(\n\t\t\t\t\tfunc() type2 { return type2{} },\n\t\t\t\t\tfunc() type3 { return type3{} },\n\t\t\t\t),\n\t\t\t),\n\t\t)\n\n\t\terr := app.Err()\n\t\trequire.Error(t, err, \"expected start failure\")\n\n\t\t// Example:\n\t\t// fx.Option should be passed to fx.New directly, not to fx.Provide: fx.Provide received fx.Provide(go.uber.org/fx_test.TestAppStart.func5.2(), go.uber.org/fx_test.TestAppStart.func5.3()) from:\n\t\t// go.uber.org/fx_test.TestAppStart.func5\n\t\t//         /.../fx/app_test.go:550\n\t\t// testing.tRunner\n\t\t//         /.../go/1.13.3/libexec/src/testing/testing.go:909\n\t\tassert.Contains(t, err.Error(), \"fx.Option should be passed to fx.New directly, not to fx.Provide\")\n\t\tassert.Contains(t, err.Error(), \"fx.Provide received fx.Provide(go.uber.org/fx_test.TestAppStart\")\n\t\tassert.Contains(t, err.Error(), \"go.uber.org/fx_test.TestAppStart\")\n\t\tassert.Contains(t, err.Error(), \"/app_test.go\")\n\t})\n\n\tt.Run(\"InvokingAnInvokeShouldFail\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\ttype type1 struct{}\n\n\t\tapp := NewForTest(t,\n\t\t\tProvide(func() type1 { return type1{} }),\n\t\t\tInvoke(Invoke(func(type1) {\n\t\t\t})),\n\t\t)\n\t\tnewErr := app.Err()\n\t\trequire.Error(t, newErr)\n\n\t\terr := app.Start(context.Background())\n\t\trequire.Error(t, err, \"expected start failure\")\n\t\tassert.Equal(t, err, newErr, \"start should return the same error fx.New encountered\")\n\n\t\t// Example\n\t\t// fx.Option should be passed to fx.New directly, not to fx.Invoke: fx.Invoke received fx.Invoke(go.uber.org/fx_test.TestAppStart.func6.2()) from:\n\t\t// go.uber.org/fx_test.TestAppStart.func6\n\t\t//         /.../fx/app_test.go:579\n\t\t// testing.tRunner\n\t\t//         /.../go/1.13.3/libexec/src/testing/testing.go:909\n\t\tassert.Contains(t, err.Error(), \"fx.Option should be passed to fx.New directly, not to fx.Invoke\")\n\t\tassert.Contains(t, err.Error(), \"fx.Invoke received fx.Invoke(go.uber.org/fx_test.TestAppStart\")\n\t\tassert.Contains(t, err.Error(), \"go.uber.org/fx_test.TestAppStart\")\n\t\tassert.Contains(t, err.Error(), \"/app_test.go\")\n\t})\n\n\tt.Run(\"ProvidingOptionsShouldFail\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\ttype type1 struct{}\n\t\ttype type2 struct{}\n\t\ttype type3 struct{}\n\n\t\tmodule := Options(\n\t\t\tProvide(\n\t\t\t\tfunc() type1 { return type1{} },\n\t\t\t\tfunc() type2 { return type2{} },\n\t\t\t),\n\t\t\tInvoke(func(type1) {\n\t\t\t\trequire.FailNow(t, \"module Invoked must not be called\")\n\t\t\t}),\n\t\t)\n\n\t\tapp := NewForTest(t,\n\t\t\tProvide(\n\t\t\t\tfunc() type3 { return type3{} },\n\t\t\t\tmodule,\n\t\t\t),\n\t\t)\n\t\terr := app.Err()\n\t\trequire.Error(t, err, \"expected start failure\")\n\n\t\t// Example:\n\t\t// fx.Annotated should be passed to fx.Provide directly, it should not be returned by the constructor: fx.Provide received go.uber.org/fx_test.TestAnnotatedWrongUsage.func2.1() from:\n\t\t// go.uber.org/fx_test.TestAnnotatedWrongUsage.func2\n\t\t//         /.../fx/annotated_test.go:76\n\t\t// testing.tRunner\n\t\t//         /.../go/1.13.3/libexec/src/testing/testing.go:909\n\t\tassert.Contains(t, err.Error(), \"fx.Option should be passed to fx.New directly, not to fx.Provide\")\n\t\tassert.Contains(t, err.Error(), \"fx.Provide received fx.Options(fx.Provide(go.uber.org/fx_test.TestAppStart\")\n\t\tassert.Contains(t, err.Error(), \"go.uber.org/fx_test.TestAppStart\")\n\t\tassert.Contains(t, err.Error(), \"/app_test.go\")\n\t})\n\n\tt.Run(\"HookGoroutineExitsErrorMsg\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\taddHook := func(lc Lifecycle) {\n\t\t\tlc.Append(Hook{\n\t\t\t\tOnStart: func(ctx context.Context) error {\n\t\t\t\t\truntime.Goexit()\n\t\t\t\t\treturn nil\n\t\t\t\t},\n\t\t\t})\n\t\t}\n\t\tapp := fxtest.New(t,\n\t\t\tInvoke(addHook),\n\t\t)\n\t\terr := app.Start(context.Background()).Error()\n\t\tassert.Contains(t, \"goroutine exited without returning\", err)\n\t})\n\n\tt.Run(\"StartTwiceWithHooksErrors\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tctx := t.Context()\n\n\t\tapp := fxtest.New(t,\n\t\t\tInvoke(func(lc Lifecycle) {\n\t\t\t\tlc.Append(Hook{\n\t\t\t\t\tOnStart: func(ctx context.Context) error { return nil },\n\t\t\t\t\tOnStop:  func(ctx context.Context) error { return nil },\n\t\t\t\t})\n\t\t\t}),\n\t\t)\n\t\tassert.NoError(t, app.Start(ctx))\n\t\terr := app.Start(ctx)\n\t\tif assert.Error(t, err) {\n\t\t\tassert.ErrorContains(t, err, \"attempted to start lifecycle when in state: started\")\n\t\t}\n\t\tapp.Stop(ctx)\n\t\tassert.NoError(t, app.Start(ctx))\n\t\tapp.Stop(ctx)\n\t})\n}\n\nfunc TestAppStop(t *testing.T) {\n\tt.Parallel()\n\n\tt.Run(\"Timeout\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tmockClock := fxclock.NewMock()\n\n\t\tblock := func(ctx context.Context) error {\n\t\t\tmockClock.Add(5 * time.Second)\n\t\t\treturn ctx.Err()\n\t\t}\n\t\t// NOTE: for tests that gets cancelled/times out during lifecycle methods, it's possible\n\t\t// for them to run into race with fxevent logs getting written to testing.T with the\n\t\t// remainder of the tests. As a workaround, we provide fxlog.Spy to prevent the lifecycle\n\t\t// goroutine from writing to testing.T.\n\t\tspy := new(fxlog.Spy)\n\t\tapp := New(Invoke(func(l Lifecycle) { l.Append(Hook{OnStop: block}) }),\n\t\t\tWithLogger(func() fxevent.Logger { return spy }),\n\t\t\tWithClock(mockClock),\n\t\t)\n\n\t\terr := app.Start(context.Background())\n\t\trequire.Nil(t, err)\n\n\t\tctx, cancel := mockClock.WithTimeout(context.Background(), time.Second)\n\t\tdefer cancel()\n\n\t\terr = app.Stop(ctx)\n\t\trequire.Error(t, err)\n\t\tassert.Contains(t, err.Error(), \"context deadline exceeded\")\n\t})\n\n\tt.Run(\"StopError\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tfailStop := func(lc Lifecycle) struct{} {\n\t\t\tlc.Append(Hook{OnStop: func(context.Context) error {\n\t\t\t\treturn errors.New(\"OnStop fail\")\n\t\t\t}})\n\t\t\treturn struct{}{}\n\t\t}\n\t\tapp := fxtest.New(t,\n\t\t\tProvide(failStop),\n\t\t\tInvoke(func(struct{}) {}),\n\t\t)\n\t\tapp.RequireStart()\n\t\terr := app.Stop(context.Background())\n\t\trequire.Error(t, err)\n\t\tassert.Contains(t, err.Error(), \"OnStop fail\")\n\t})\n}\n\nfunc TestValidateApp(t *testing.T) {\n\tt.Parallel()\n\n\t// helper to use the test logger\n\tvalidateApp := func(t *testing.T, opts ...Option) error {\n\t\treturn ValidateApp(\n\t\t\tappend(opts, Logger(fxtest.NewTestPrinter(t)))...,\n\t\t)\n\t}\n\n\tt.Run(\"do not run provides on graph validation\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\ttype type1 struct{}\n\t\terr := validateApp(t,\n\t\t\tProvide(func() *type1 {\n\t\t\t\tt.Error(\"provide must not be called\")\n\t\t\t\treturn nil\n\t\t\t}),\n\t\t\tInvoke(func(*type1) {}),\n\t\t)\n\t\trequire.NoError(t, err)\n\t})\n\tt.Run(\"do not run provides nor invokes on graph validation\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\ttype type1 struct{}\n\t\terr := validateApp(t,\n\t\t\tProvide(func() *type1 {\n\t\t\t\tt.Error(\"provide must not be called\")\n\t\t\t\treturn nil\n\t\t\t}),\n\t\t\tInvoke(func(*type1) {\n\t\t\t\tt.Error(\"invoke must not be called\")\n\t\t\t}),\n\t\t)\n\t\trequire.NoError(t, err)\n\t})\n\tt.Run(\"provide depends on something not available\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\ttype type1 struct{}\n\t\terr := validateApp(t,\n\t\t\tProvide(func(type1) int { return 0 }),\n\t\t\tInvoke(func(int) error { return nil }),\n\t\t)\n\t\trequire.Error(t, err, \"fx.ValidateApp should error on argument not available\")\n\t\terrMsg := err.Error()\n\t\tassert.Contains(t, errMsg, \"could not build arguments for function\")\n\t\tassert.Contains(t, errMsg, \"failed to build int: missing dependencies for function\")\n\t\tassert.Contains(t, errMsg, \"missing type: fx_test.type1\")\n\t})\n\tt.Run(\"provide introduces a cycle\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\ttype A struct{}\n\t\ttype B struct{}\n\t\terr := validateApp(t,\n\t\t\tProvide(func(A) B { return B{} }),\n\t\t\tProvide(func(B) A { return A{} }),\n\t\t\tInvoke(func(B) {}),\n\t\t)\n\t\trequire.Error(t, err, \"fx.ValidateApp should error on cycle\")\n\t\terrMsg := err.Error()\n\t\tassert.Contains(t, errMsg, \"cycle detected in dependency graph\")\n\t})\n\tt.Run(\"invoke a type that's not available\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\ttype A struct{}\n\t\terr := validateApp(t,\n\t\t\tInvoke(func(A) {}),\n\t\t)\n\t\trequire.Error(t, err, \"fx.ValidateApp should return an error on missing invoke dep\")\n\t\terrMsg := err.Error()\n\t\tassert.Contains(t, errMsg, \"missing dependencies for function\")\n\t\tassert.Contains(t, errMsg, \"missing type: fx_test.A\")\n\t})\n\tt.Run(\"no error\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\ttype A struct{}\n\t\terr := validateApp(t,\n\t\t\tProvide(func() A {\n\t\t\t\treturn A{}\n\t\t\t}),\n\t\t\tInvoke(func(A) {}),\n\t\t)\n\t\trequire.NoError(t, err, \"fx.ValidateApp should not return an error\")\n\t})\n}\n\nfunc TestHookConstructors(t *testing.T) {\n\tt.Run(\"all\", func(t *testing.T) {\n\t\tvar (\n\t\t\tcalls = map[string]int{\n\t\t\t\t\"start func\":         0,\n\t\t\t\t\"start func err\":     0,\n\t\t\t\t\"start ctx func\":     0,\n\t\t\t\t\"start ctx func err\": 0,\n\t\t\t\t\"stop func\":          0,\n\t\t\t\t\"stop func err\":      0,\n\t\t\t\t\"stop ctx func\":      0,\n\t\t\t\t\"stop ctx func err\":  0,\n\t\t\t}\n\t\t\tnilFunc             func()\n\t\t\tnilErrorFunc        func() error\n\t\t\tnilContextFunc      func(context.Context)\n\t\t\tnilContextErrorFunc func(context.Context) error\n\t\t)\n\n\t\tfxtest.New(\n\t\t\tt,\n\t\t\tInvoke(func(lc Lifecycle) {\n\t\t\t\t// Nil functions\n\t\t\t\tlc.Append(StartStopHook(nilFunc, nilErrorFunc))\n\t\t\t\tlc.Append(StartStopHook(nilContextFunc, nilContextErrorFunc))\n\n\t\t\t\t// Start hooks\n\t\t\t\tlc.Append(StartHook(func() {\n\t\t\t\t\tcalls[\"start func\"]++\n\t\t\t\t}))\n\t\t\t\tlc.Append(StartHook(func() error {\n\t\t\t\t\tcalls[\"start func err\"]++\n\t\t\t\t\treturn nil\n\t\t\t\t}))\n\t\t\t\tlc.Append(StartHook(func(context.Context) {\n\t\t\t\t\tcalls[\"start ctx func\"]++\n\t\t\t\t}))\n\t\t\t\tlc.Append(StartHook(func(context.Context) error {\n\t\t\t\t\tcalls[\"start ctx func err\"]++\n\t\t\t\t\treturn nil\n\t\t\t\t}))\n\n\t\t\t\t// Stop hooks\n\t\t\t\tlc.Append(StopHook(func() {\n\t\t\t\t\tcalls[\"stop func\"]++\n\t\t\t\t}))\n\t\t\t\tlc.Append(StopHook(func() error {\n\t\t\t\t\tcalls[\"stop func err\"]++\n\t\t\t\t\treturn nil\n\t\t\t\t}))\n\t\t\t\tlc.Append(StopHook(func(context.Context) {\n\t\t\t\t\tcalls[\"stop ctx func\"]++\n\t\t\t\t}))\n\t\t\t\tlc.Append(StopHook(func(context.Context) error {\n\t\t\t\t\tcalls[\"stop ctx func err\"]++\n\t\t\t\t\treturn nil\n\t\t\t\t}))\n\n\t\t\t\t// StartStop hooks\n\t\t\t\tlc.Append(StartStopHook(\n\t\t\t\t\tfunc() {\n\t\t\t\t\t\tcalls[\"start func\"]++\n\t\t\t\t\t},\n\t\t\t\t\tfunc() error {\n\t\t\t\t\t\tcalls[\"stop func err\"]++\n\t\t\t\t\t\treturn nil\n\t\t\t\t\t},\n\t\t\t\t))\n\t\t\t\tlc.Append(StartStopHook(\n\t\t\t\t\tfunc() error {\n\t\t\t\t\t\tcalls[\"start func err\"]++\n\t\t\t\t\t\treturn nil\n\t\t\t\t\t},\n\t\t\t\t\tfunc(context.Context) {\n\t\t\t\t\t\tcalls[\"stop ctx func\"]++\n\t\t\t\t\t},\n\t\t\t\t))\n\t\t\t\tlc.Append(StartStopHook(\n\t\t\t\t\tfunc(context.Context) {\n\t\t\t\t\t\tcalls[\"start ctx func\"]++\n\t\t\t\t\t},\n\t\t\t\t\tfunc(context.Context) error {\n\t\t\t\t\t\tcalls[\"stop ctx func err\"]++\n\t\t\t\t\t\treturn nil\n\t\t\t\t\t},\n\t\t\t\t))\n\t\t\t\tlc.Append(StartStopHook(\n\t\t\t\t\tfunc(context.Context) error {\n\t\t\t\t\t\tcalls[\"start ctx func err\"]++\n\t\t\t\t\t\treturn nil\n\t\t\t\t\t},\n\t\t\t\t\tfunc() {\n\t\t\t\t\t\tcalls[\"stop func\"]++\n\t\t\t\t\t},\n\t\t\t\t))\n\t\t\t}),\n\t\t).RequireStart().RequireStop()\n\n\t\tfor name, count := range calls {\n\t\t\trequire.Equalf(t, 2, count, \"bad call count: %s (%d)\", name, count)\n\t\t}\n\t})\n\n\tt.Run(\"start errors\", func(t *testing.T) {\n\t\twantErr := errors.New(\"oh no\")\n\n\t\t// Verify that wrapped `func() error` funcs produce the expected error.\n\t\terr := New(Invoke(func(lc Lifecycle) {\n\t\t\tlc.Append(StartHook(func() error {\n\t\t\t\treturn wantErr\n\t\t\t}))\n\t\t})).Start(context.Background())\n\t\trequire.ErrorContains(t, err, wantErr.Error())\n\n\t\t// Verify that wrapped `func(context.Context) error` funcs produce the\n\t\t// expected error.\n\t\terr = New(Invoke(func(lc Lifecycle) {\n\t\t\tlc.Append(StartHook(func(ctx context.Context) error {\n\t\t\t\treturn wantErr\n\t\t\t}))\n\t\t})).Start(context.Background())\n\t\trequire.ErrorContains(t, err, wantErr.Error())\n\t})\n\n\tt.Run(\"start deadline\", func(t *testing.T) {\n\t\twantCtx, cancel := context.WithTimeout(context.Background(), 123*time.Second)\n\t\tdefer cancel()\n\n\t\t// Verify that `func(context.Context)` funcs receive the expected\n\t\t// deadline.\n\t\tapp := New(Invoke(func(lc Lifecycle) {\n\t\t\tlc.Append(StartHook(func(ctx context.Context) {\n\t\t\t\tvar (\n\t\t\t\t\twant, wantOK = wantCtx.Deadline()\n\t\t\t\t\tgive, giveOK = ctx.Deadline()\n\t\t\t\t)\n\n\t\t\t\trequire.Equal(t, wantOK, giveOK)\n\t\t\t\trequire.True(t, want.Equal(give))\n\t\t\t}))\n\t\t}))\n\t\trequire.NoError(t, app.Start(wantCtx))\n\t\trequire.NoError(t, app.Stop(wantCtx))\n\n\t\t// Verify that `func(context.Context) error` funcs receive the expected\n\t\t// deadline.\n\t\tapp = New(Invoke(func(lc Lifecycle) {\n\t\t\tlc.Append(StartHook(func(ctx context.Context) error {\n\t\t\t\tvar (\n\t\t\t\t\twant, wantOK = wantCtx.Deadline()\n\t\t\t\t\tgive, giveOK = ctx.Deadline()\n\t\t\t\t)\n\n\t\t\t\trequire.Equal(t, wantOK, giveOK)\n\t\t\t\trequire.True(t, want.Equal(give))\n\n\t\t\t\treturn nil\n\t\t\t}))\n\t\t}))\n\t\trequire.NoError(t, app.Start(wantCtx))\n\t\trequire.NoError(t, app.Stop(wantCtx))\n\t})\n\n\tt.Run(\"stop errors\", func(t *testing.T) {\n\t\tvar (\n\t\t\tctx     = context.Background()\n\t\t\twantErr = errors.New(\"oh no\")\n\t\t)\n\n\t\t// Verify that wrapped `func() error` funcs produce the expected error.\n\t\tapp := New(Invoke(func(lc Lifecycle) {\n\t\t\tlc.Append(StopHook(func() error {\n\t\t\t\treturn wantErr\n\t\t\t}))\n\t\t}))\n\t\trequire.NoError(t, app.Start(ctx))\n\t\trequire.ErrorContains(t, app.Stop(ctx), wantErr.Error())\n\n\t\t// Verify that wrapped `func(context.Context) error` funcs produce the\n\t\t// expected error.\n\t\tapp = New(Invoke(func(lc Lifecycle) {\n\t\t\tlc.Append(StopHook(func(ctx context.Context) error {\n\t\t\t\treturn wantErr\n\t\t\t}))\n\t\t}))\n\t\trequire.NoError(t, app.Start(ctx))\n\t\trequire.ErrorIs(t, app.Stop(ctx), wantErr)\n\t})\n\n\tt.Run(\"stop deadline\", func(t *testing.T) {\n\t\twantCtx, cancel := context.WithTimeout(\n\t\t\tcontext.Background(),\n\t\t\t123*time.Second,\n\t\t)\n\t\tdefer cancel()\n\n\t\t// Verify that `func(context.Context)` funcs receive the expected\n\t\t// deadline.\n\t\tapp := New(Invoke(func(lc Lifecycle) {\n\t\t\tlc.Append(StopHook(func(ctx context.Context) {\n\t\t\t\tvar (\n\t\t\t\t\twant, wantOK = wantCtx.Deadline()\n\t\t\t\t\tgive, giveOK = ctx.Deadline()\n\t\t\t\t)\n\n\t\t\t\trequire.Equal(t, wantOK, giveOK)\n\t\t\t\trequire.True(t, want.Equal(give))\n\t\t\t}))\n\t\t}))\n\t\trequire.NoError(t, app.Start(wantCtx))\n\t\trequire.NoError(t, app.Stop(wantCtx))\n\n\t\t// Verify that `func(context.Context) error` funcs receive the expected\n\t\t// deadline.\n\t\tapp = New(Invoke(func(lc Lifecycle) {\n\t\t\tlc.Append(StopHook(func(ctx context.Context) error {\n\t\t\t\tvar (\n\t\t\t\t\twant, wantOK = wantCtx.Deadline()\n\t\t\t\t\tgive, giveOK = ctx.Deadline()\n\t\t\t\t)\n\n\t\t\t\trequire.Equal(t, wantOK, giveOK)\n\t\t\t\trequire.True(t, want.Equal(give))\n\n\t\t\t\treturn nil\n\t\t\t}))\n\t\t}))\n\t\trequire.NoError(t, app.Start(wantCtx))\n\t\trequire.NoError(t, app.Stop(wantCtx))\n\t})\n\n\tt.Run(\"typed\", func(t *testing.T) {\n\t\ttype (\n\t\t\tfuncType       func()\n\t\t\tfuncErrType    func() error\n\t\t\tctxFuncType    func(context.Context)\n\t\t\tctxFuncErrType func(context.Context) error\n\t\t)\n\n\t\tvar (\n\t\t\tcalls  int\n\t\t\tmyFunc = funcType(func() {\n\t\t\t\tcalls++\n\t\t\t})\n\t\t\tmyFuncErr = funcErrType(func() error {\n\t\t\t\tcalls++\n\t\t\t\treturn nil\n\t\t\t})\n\t\t\tmyCtxFunc = ctxFuncType(func(context.Context) {\n\t\t\t\tcalls++\n\t\t\t})\n\t\t\tmyCtxFuncErr = ctxFuncErrType(func(context.Context) error {\n\t\t\t\tcalls++\n\t\t\t\treturn nil\n\t\t\t})\n\t\t)\n\n\t\t// Ensure that user function types that are assignable to supported\n\t\t// base function types are converted as expected.\n\t\trequire.NotPanics(t, func() {\n\t\t\tfxtest.New(t, Invoke(func(lc Lifecycle) {\n\t\t\t\tlc.Append(StartStopHook(myFunc, myFuncErr))\n\t\t\t\tlc.Append(StartStopHook(myCtxFunc, myCtxFuncErr))\n\t\t\t})).RequireStart().RequireStop()\n\t\t})\n\t\trequire.Equal(t, 4, calls)\n\t})\n}\n\nfunc TestDone(t *testing.T) {\n\tt.Parallel()\n\n\tapp := fxtest.New(t)\n\tdefer app.RequireStop()\n\tdone := app.Done()\n\trequire.NotNil(t, done, \"Got a nil channel.\")\n\tselect {\n\tcase sig := <-done:\n\t\tt.Fatalf(\"Got unexpected signal %v from application's Done channel.\", sig)\n\tdefault:\n\t}\n}\n\n// TestShutdownThenWait tests that if we call .Shutdown before waiting, the wait\n// will still return the last shutdown signal.\nfunc TestShutdownThenWait(t *testing.T) {\n\tt.Parallel()\n\n\tvar (\n\t\ts       Shutdowner\n\t\tstopped bool\n\t)\n\tapp := fxtest.New(\n\t\tt,\n\t\tPopulate(&s),\n\t\tInvoke(func(lc Lifecycle) {\n\t\t\tlc.Append(StopHook(func() {\n\t\t\t\tstopped = true\n\t\t\t}))\n\t\t}),\n\t).RequireStart()\n\trequire.NotNil(t, s)\n\n\terr := s.Shutdown(ExitCode(1337))\n\tassert.NoError(t, err)\n\tassert.False(t, stopped)\n\n\tshutdownSig := <-app.Wait()\n\tassert.Equal(t, 1337, shutdownSig.ExitCode)\n\tassert.False(t, stopped)\n\n\tapp.RequireStop()\n\tassert.True(t, stopped)\n}\n\nfunc TestReplaceLogger(t *testing.T) {\n\tt.Parallel()\n\n\tspy := new(fxlog.Spy)\n\tapp := fxtest.New(t, WithLogger(func() fxevent.Logger { return spy }))\n\tapp.RequireStart().RequireStop()\n\tassert.Equal(t, []string{\n\t\t\"Provided\",\n\t\t\"Provided\",\n\t\t\"Provided\",\n\t\t\"LoggerInitialized\",\n\t\t\"Started\",\n\t\t\"Stopped\",\n\t}, spy.EventTypes())\n}\n\nfunc TestNopLogger(t *testing.T) {\n\tt.Parallel()\n\n\tapp := fxtest.New(t, NopLogger)\n\tapp.RequireStart().RequireStop()\n}\n\nfunc TestNopLoggerOptionString(t *testing.T) {\n\tt.Parallel()\n\n\tassert.Equal(t,\n\t\t\"fx.WithLogger(go.uber.org/fx.init.func1())\",\n\t\tNopLogger.String(),\n\t)\n}\n\nfunc TestCustomLoggerWithPrinter(t *testing.T) {\n\tt.Parallel()\n\n\t// If we provide both, an fx.Logger and fx.WithLogger, and the logger\n\t// fails, we should fall back to the fx.Logger.\n\n\tvar buff bytes.Buffer\n\tapp := New(\n\t\tLogger(log.New(&buff, \"\", 0)),\n\t\tWithLogger(func() (fxevent.Logger, error) {\n\t\t\treturn nil, errors.New(\"great sadness\")\n\t\t}),\n\t)\n\terr := app.Start(context.Background())\n\trequire.Error(t, err)\n\tassert.Contains(t, err.Error(), \"great sadness\")\n\n\tout := buff.String()\n\tassert.Contains(t, out, \"failed to build fxevent.Logger\")\n\tassert.Contains(t, out, \"great sadness\")\n}\n\nfunc TestCustomLoggerWithLifecycle(t *testing.T) {\n\tt.Parallel()\n\n\tvar started, stopped bool\n\tdefer func() {\n\t\tassert.True(t, started, \"never started\")\n\t\tassert.True(t, stopped, \"never stopped\")\n\t}()\n\n\tvar buff bytes.Buffer\n\tdefer func() {\n\t\tassert.Empty(t, buff.String(), \"unexpectedly wrote to the fallback logger\")\n\t}()\n\n\tvar spy fxlog.Spy\n\tapp := New(\n\t\t// We expect WithLogger to do its job. This means we shouldn't\n\t\t// print anything to this buffer.\n\t\tLogger(log.New(&buff, \"\", 0)),\n\t\tWithLogger(func(lc Lifecycle) fxevent.Logger {\n\t\t\tlc.Append(Hook{\n\t\t\t\tOnStart: func(context.Context) error {\n\t\t\t\t\tassert.False(t, started, \"started twice\")\n\t\t\t\t\tstarted = true\n\t\t\t\t\treturn nil\n\t\t\t\t},\n\t\t\t\tOnStop: func(context.Context) error {\n\t\t\t\t\tassert.False(t, stopped, \"stopped twice\")\n\t\t\t\t\tstopped = true\n\t\t\t\t\treturn nil\n\t\t\t\t},\n\t\t\t})\n\t\t\treturn &spy\n\t\t}),\n\t)\n\n\trequire.NoError(t, app.Start(context.Background()))\n\trequire.NoError(t, app.Stop(context.Background()))\n\n\tassert.Equal(t, []string{\n\t\t\"Provided\",\n\t\t\"Provided\",\n\t\t\"Provided\",\n\t\t\"BeforeRun\",\n\t\t\"Run\",\n\t\t\"LoggerInitialized\",\n\t\t\"OnStartExecuting\", \"OnStartExecuted\",\n\t\t\"Started\",\n\t\t\"OnStopExecuting\", \"OnStopExecuted\",\n\t\t\"Stopped\",\n\t}, spy.EventTypes())\n}\n\nfunc TestCustomLoggerFailure(t *testing.T) {\n\tt.Parallel()\n\n\tvar buff bytes.Buffer\n\tapp := New(\n\t\t// We expect WithLogger to fail, so this buffer should be\n\t\t// contain information about the failure.\n\t\tLogger(log.New(&buff, \"\", 0)),\n\t\tWithLogger(func() (fxevent.Logger, error) {\n\t\t\treturn nil, errors.New(\"great sadness\")\n\t\t}),\n\t)\n\trequire.Error(t, app.Err())\n\n\tout := buff.String()\n\tassert.Contains(t, out, \"Failed to initialize custom logger\")\n\tassert.Contains(t, out, \"failed to build fxevent.Logger\")\n\tassert.Contains(t, out, \"received non-nil error from function\")\n\tassert.Contains(t, out, \"great sadness\")\n}\n\ntype testErrorWithGraph struct {\n\tgraph string\n}\n\nfunc (we testErrorWithGraph) Graph() DotGraph {\n\treturn DotGraph(we.graph)\n}\n\nfunc (we testErrorWithGraph) Error() string {\n\treturn \"great sadness\"\n}\n\nfunc TestVisualizeError(t *testing.T) {\n\tt.Parallel()\n\n\tt.Run(\"NotWrappedError\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\t_, err := VisualizeError(errors.New(\"great sadness\"))\n\t\trequire.Error(t, err)\n\t})\n\n\tt.Run(\"WrappedErrorWithEmptyGraph\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tgraph, err := VisualizeError(testErrorWithGraph{graph: \"\"})\n\t\tassert.Empty(t, graph)\n\t\trequire.Error(t, err)\n\t})\n\n\tt.Run(\"WrappedError\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tgraph, err := VisualizeError(testErrorWithGraph{graph: \"graph\"})\n\t\tassert.Equal(t, \"graph\", graph)\n\t\trequire.NoError(t, err)\n\t})\n}\n\nfunc TestErrorHook(t *testing.T) {\n\tt.Parallel()\n\n\tt.Run(\"UnvisualizableError\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\ttype A struct{}\n\n\t\tvar graphErr error\n\t\th := errHandlerFunc(func(err error) {\n\t\t\t_, graphErr = VisualizeError(err)\n\t\t})\n\t\tNewForTest(t,\n\t\t\tProvide(func() A { return A{} }),\n\t\t\tInvoke(func(A) error { return errors.New(\"great sadness\") }),\n\t\t\tErrorHook(h),\n\t\t)\n\t\tassert.Equal(t, errors.New(\"unable to visualize error\"), graphErr)\n\t})\n\n\tt.Run(\"GraphWithError\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\ttype A struct{}\n\t\ttype B struct{}\n\n\t\tvar errStr, graphStr string\n\t\th := errHandlerFunc(func(err error) {\n\t\t\terrStr = err.Error()\n\t\t\tgraphStr, _ = VisualizeError(err)\n\t\t})\n\t\tNewForTest(t,\n\t\t\tProvide(func() (B, error) { return B{}, fmt.Errorf(\"great sadness\") }),\n\t\t\tProvide(func(B) A { return A{} }),\n\t\t\tInvoke(func(A) {}),\n\t\t\tErrorHook(&h),\n\t\t)\n\t\tassert.Contains(t, errStr, \"great sadness\")\n\t\tassert.Contains(t, graphStr, `\"fx_test.B\" [color=red];`)\n\t\tassert.Contains(t, graphStr, `\"fx_test.A\" [color=orange];`)\n\t})\n\n\tt.Run(\"GraphWithErrorInModule\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\ttype A struct{}\n\t\ttype B struct{}\n\n\t\tvar errStr, graphStr string\n\t\th := errHandlerFunc(func(err error) {\n\t\t\terrStr = err.Error()\n\t\t\tgraphStr, _ = VisualizeError(err)\n\t\t})\n\t\tNewForTest(t,\n\t\t\tModule(\"module\",\n\t\t\t\tProvide(func() (B, error) { return B{}, fmt.Errorf(\"great sadness\") }),\n\t\t\t\tProvide(func(B) A { return A{} }),\n\t\t\t\tInvoke(func(A) {}),\n\t\t\t\tErrorHook(&h),\n\t\t\t),\n\t\t)\n\t\tassert.Contains(t, errStr, \"great sadness\")\n\t\tassert.Contains(t, graphStr, `\"fx_test.B\" [color=red];`)\n\t\tassert.Contains(t, graphStr, `\"fx_test.A\" [color=orange];`)\n\t})\n}\n\nfunc TestOptionString(t *testing.T) {\n\tt.Parallel()\n\n\ttests := []struct {\n\t\tdesc string\n\t\tgive Option\n\t\twant string\n\t}{\n\t\t{\n\t\t\tdesc: \"Provide\",\n\t\t\tgive: Provide(bytes.NewReader),\n\t\t\twant: \"fx.Provide(bytes.NewReader())\",\n\t\t},\n\t\t{\n\t\t\tdesc: \"Invoked\",\n\t\t\tgive: Invoke(func(c io.Closer) error {\n\t\t\t\treturn c.Close()\n\t\t\t}),\n\t\t\twant: \"fx.Invoke(go.uber.org/fx_test.TestOptionString.func1())\",\n\t\t},\n\t\t{\n\t\t\tdesc: \"Error/single\",\n\t\t\tgive: Error(errors.New(\"great sadness\")),\n\t\t\twant: \"fx.Error(great sadness)\",\n\t\t},\n\t\t{\n\t\t\tdesc: \"Error/multiple\",\n\t\t\tgive: Error(errors.New(\"foo\"), errors.New(\"bar\")),\n\t\t\twant: \"fx.Error(foo; bar)\",\n\t\t},\n\t\t{\n\t\t\tdesc: \"Options/single\",\n\t\t\tgive: Options(Provide(bytes.NewBuffer)),\n\t\t\t// NOTE: We don't prune away fx.Options for the empty\n\t\t\t// case because we want to attach additional\n\t\t\t// information to the fx.Options object in the future.\n\t\t\twant: \"fx.Options(fx.Provide(bytes.NewBuffer()))\",\n\t\t},\n\t\t{\n\t\t\tdesc: \"Options/multiple\",\n\t\t\tgive: Options(\n\t\t\t\tProvide(bytes.NewBufferString),\n\t\t\t\tInvoke(func(buf *bytes.Buffer) {\n\t\t\t\t\tbuf.WriteString(\"hello\")\n\t\t\t\t}),\n\t\t\t),\n\t\t\twant: \"fx.Options(\" +\n\t\t\t\t\"fx.Provide(bytes.NewBufferString()), \" +\n\t\t\t\t\"fx.Invoke(go.uber.org/fx_test.TestOptionString.func2())\" +\n\t\t\t\t\")\",\n\t\t},\n\t\t{\n\t\t\tdesc: \"StartTimeout\",\n\t\t\tgive: StartTimeout(time.Second),\n\t\t\twant: \"fx.StartTimeout(1s)\",\n\t\t},\n\t\t{\n\t\t\tdesc: \"StopTimeout\",\n\t\t\tgive: StopTimeout(5 * time.Second),\n\t\t\twant: \"fx.StopTimeout(5s)\",\n\t\t},\n\t\t{\n\t\t\tdesc: \"RecoverFromPanics\",\n\t\t\tgive: RecoverFromPanics(),\n\t\t\twant: \"fx.RecoverFromPanics()\",\n\t\t},\n\t\t{\n\t\t\tdesc: \"Logger\",\n\t\t\tgive: WithLogger(func() fxevent.Logger { return testLogger{t} }),\n\t\t\twant: \"fx.WithLogger(go.uber.org/fx_test.TestOptionString.func3())\",\n\t\t},\n\t\t{\n\t\t\tdesc: \"ErrorHook\",\n\t\t\tgive: ErrorHook(testErrorHandler{t}),\n\t\t\twant: \"fx.ErrorHook(TestOptionString)\",\n\t\t},\n\t\t{\n\t\t\tdesc: \"Supplied/simple\",\n\t\t\tgive: Supply(bytes.NewReader(nil), bytes.NewBuffer(nil)),\n\t\t\twant: \"fx.Supply(*bytes.Reader, *bytes.Buffer)\",\n\t\t},\n\t\t{\n\t\t\tdesc: \"Supplied/Annotated\",\n\t\t\tgive: Supply(Annotated{Target: bytes.NewReader(nil)}),\n\t\t\twant: \"fx.Supply(*bytes.Reader)\",\n\t\t},\n\t\t{\n\t\t\tdesc: \"Decorate\",\n\t\t\tgive: Decorate(bytes.NewBufferString),\n\t\t\twant: \"fx.Decorate(bytes.NewBufferString())\",\n\t\t},\n\t\t{\n\t\t\tdesc: \"Replace\",\n\t\t\tgive: Replace(bytes.NewReader(nil)),\n\t\t\twant: \"fx.Replace(*bytes.Reader)\",\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.desc, func(t *testing.T) {\n\t\t\tt.Parallel()\n\n\t\t\tstringer, ok := tt.give.(fmt.Stringer)\n\t\t\trequire.True(t, ok, \"option must implement stringer\")\n\t\t\tassert.Equal(t, tt.want, stringer.String())\n\t\t})\n\t}\n}\n\nfunc TestMain(m *testing.M) {\n\tgoleak.VerifyTestMain(m)\n}\n\ntype testLogger struct{ t *testing.T }\n\nfunc (l testLogger) Printf(s string, args ...any) {\n\tl.t.Logf(s, args...)\n}\n\nfunc (l testLogger) LogEvent(event fxevent.Event) {\n\tl.t.Logf(\"emitted event %#v\", event)\n}\n\nfunc (l testLogger) String() string {\n\treturn l.t.Name()\n}\n\ntype testErrorHandler struct{ t *testing.T }\n\nfunc (h testErrorHandler) HandleError(err error) {\n\th.t.Error(err)\n}\n\nfunc (h testErrorHandler) String() string {\n\treturn h.t.Name()\n}\n"
  },
  {
    "path": "app_unixes.go",
    "content": "// Copyright (c) 2021 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\n//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris\n// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris\n\npackage fx\n\nimport \"golang.org/x/sys/unix\"\n\nconst (\n\t_sigINT  = unix.SIGINT\n\t_sigTERM = unix.SIGTERM\n)\n"
  },
  {
    "path": "app_wasm.go",
    "content": "// Copyright (c) 2023 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\n//go:build (js && wasm) || (wasip1 && wasm)\n// +build js,wasm wasip1,wasm\n\npackage fx\n\nimport \"syscall\"\n\nconst (\n\t_sigINT  = syscall.SIGINT\n\t_sigTERM = syscall.SIGTERM\n)\n"
  },
  {
    "path": "app_windows.go",
    "content": "// Copyright (c) 2021 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\n//go:build windows\n// +build windows\n\npackage fx\n\nimport \"golang.org/x/sys/windows\"\n\nconst (\n\t_sigINT  = windows.SIGINT\n\t_sigTERM = windows.SIGTERM\n)\n"
  },
  {
    "path": "app_windows_test.go",
    "content": "// Copyright (c) 2020-2021 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\n//go:build windows\n// +build windows\n\npackage fx_test\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"io\"\n\t\"os\"\n\t\"os/exec\"\n\t\"syscall\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\t\"go.uber.org/fx\"\n\t\"golang.org/x/sys/windows\"\n)\n\n// Regression test for https://github.com/uber-go/fx/issues/781.\nfunc TestWindowsCtrlCHandler(t *testing.T) {\n\t// This test operates by launching a separate process,\n\t// which we'll send a SIGINT to,\n\t// and verifying the output of the application.\n\n\t// Launch a separate process we will send SIGINT to.\n\ttestExe, err := os.Executable()\n\trequire.NoError(t, err, \"determine test executable\")\n\tcmd := exec.Command(testExe, \"-test.run\", \"TestWindowsMinimalApp\")\n\tcmd.Env = append(os.Environ(), \"FX_TEST_FAKE=1\")\n\n\t// On Windows, we need to use GenerateConsoleCtrlEvent\n\t// to SIGINT the child process.\n\t//\n\t// That API operates on Group ID granularity,\n\t// so we need to make sure our new child process\n\t// gets a new group ID rather than using the same ID\n\t// as the test we're running.\n\tcmd.SysProcAttr = &syscall.SysProcAttr{\n\t\tCreationFlags: windows.CREATE_NEW_PROCESS_GROUP,\n\t}\n\n\tstdout, err := cmd.StdoutPipe()\n\trequire.NoError(t, err, \"create stdout\")\n\n\tstderr, err := cmd.StderrPipe()\n\trequire.NoError(t, err, \"create stderr\")\n\n\trequire.NoError(t, cmd.Start())\n\n\t// Block until the child is ready by waiting for the \"ready\" text\n\t// printed to stderr.\n\tready := make(chan struct{})\n\tgo func() {\n\t\tdefer close(ready)\n\t\tstderr.Read(make([]byte, 1024))\n\t}()\n\t<-ready\n\n\trequire.NoError(t,\n\t\twindows.GenerateConsoleCtrlEvent(1, uint32(cmd.Process.Pid)),\n\t\t\"SIGINT child process\")\n\n\t// Drain stdout and stderr, and wait for the process to exit.\n\toutput, err := io.ReadAll(stdout)\n\trequire.NoError(t, err)\n\t_, err = io.Copy(io.Discard, stderr)\n\trequire.NoError(t, err)\n\trequire.NoError(t, cmd.Wait())\n\n\tassert.Contains(t, string(output), \"ONSTOP\",\n\t\t\"stdout should include ONSTOP\")\n}\n\nfunc TestWindowsMinimalApp(t *testing.T) {\n\t// This is not a real test.\n\t// It defines the behavior of the fake application\n\t// that we spawn from TestWindowsCtrlCHandler.\n\tif os.Getenv(\"FX_TEST_FAKE\") != \"1\" {\n\t\treturn\n\t}\n\n\t// An Fx application that prints \"ready\" to stderr\n\t// once its start hooks have been invoked,\n\t// and \"ONSTOP\" to stdout when its stop hooks have been invoked.\n\tfx.New(\n\t\tfx.NopLogger,\n\t\tfx.Invoke(func(lifecycle fx.Lifecycle) {\n\t\t\tlifecycle.Append(fx.Hook{\n\t\t\t\tOnStart: func(ctx context.Context) error {\n\t\t\t\t\tfmt.Fprintln(os.Stderr, \"ready\")\n\t\t\t\t\treturn nil\n\t\t\t\t},\n\t\t\t\tOnStop: func(ctx context.Context) error {\n\t\t\t\t\tfmt.Fprintln(os.Stdout, \"ONSTOP\")\n\t\t\t\t\treturn nil\n\t\t\t\t},\n\t\t\t})\n\t\t}),\n\t).Run()\n}\n"
  },
  {
    "path": "broadcast.go",
    "content": "// Copyright (c) 2024 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage fx\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"sync\"\n)\n\n// broadcaster broadcasts signals to registered signal listeners.\n// All methods on the broadcaster are concurrency-safe.\ntype broadcaster struct {\n\t// This lock is used to protect all fields of broadcaster.\n\t// Methods on broadcaster should protect all concurrent access\n\t// by taking this lock when accessing its fields.\n\t// Conversely, this lock should NOT be taken outside of broadcaster.\n\tm sync.Mutex\n\n\t// last will contain a pointer to the last ShutdownSignal received, or\n\t// nil if none, if a new channel is created by Wait or Done, this last\n\t// signal will be immediately written to, this allows Wait or Done state\n\t// to be read after application stop\n\tlast *ShutdownSignal\n\n\t// contains channels created by Done\n\tdone []chan os.Signal\n\n\t// contains channels created by Wait\n\twait []chan ShutdownSignal\n}\n\nfunc (b *broadcaster) reset() {\n\tb.m.Lock()\n\tdefer b.m.Unlock()\n\tb.last = nil\n}\n\n// Done creates a new channel that will receive signals being broadcast\n// via the broadcaster.\n//\n// If a signal has been received prior to the call of Done,\n// the signal will be sent to the new channel.\nfunc (b *broadcaster) Done() <-chan os.Signal {\n\tb.m.Lock()\n\tdefer b.m.Unlock()\n\n\tch := make(chan os.Signal, 1)\n\t// If we had received a signal prior to the call of done, send it's\n\t// os.Signal to the new channel.\n\t// However we still want to have the operating system notify signals to this\n\t// channel should the application receive another.\n\tif b.last != nil {\n\t\tch <- b.last.Signal\n\t}\n\tb.done = append(b.done, ch)\n\treturn ch\n}\n\n// Wait creates a new channel that will receive signals being broadcast\n// via the broadcaster.\n//\n// If a signal has been received prior to the call of Wait,\n// the signal will be sent to the new channel.\nfunc (b *broadcaster) Wait() <-chan ShutdownSignal {\n\tb.m.Lock()\n\tdefer b.m.Unlock()\n\n\tch := make(chan ShutdownSignal, 1)\n\n\tif b.last != nil {\n\t\tch <- *b.last\n\t}\n\n\tb.wait = append(b.wait, ch)\n\treturn ch\n}\n\n// Broadcast sends the given signal to all channels that have been created\n// via Done or Wait. It does not block on sending, and returns an unsentSignalError\n// if any send did not go through.\nfunc (b *broadcaster) Broadcast(signal ShutdownSignal) error {\n\tb.m.Lock()\n\tdefer b.m.Unlock()\n\n\tb.last = &signal\n\n\tchannels, unsent := b.broadcast(\n\t\tsignal,\n\t\tb.broadcastDone,\n\t\tb.broadcastWait,\n\t)\n\n\tif unsent != 0 {\n\t\treturn &unsentSignalError{\n\t\t\tSignal: signal,\n\t\t\tTotal:  channels,\n\t\t\tUnsent: unsent,\n\t\t}\n\t}\n\n\treturn nil\n}\n\nfunc (b *broadcaster) broadcast(\n\tsignal ShutdownSignal,\n\tanchors ...func(ShutdownSignal) (int, int),\n) (int, int) {\n\tvar channels, unsent int\n\n\tfor _, anchor := range anchors {\n\t\tc, u := anchor(signal)\n\t\tchannels += c\n\t\tunsent += u\n\t}\n\n\treturn channels, unsent\n}\n\nfunc (b *broadcaster) broadcastDone(signal ShutdownSignal) (int, int) {\n\tvar unsent int\n\n\tfor _, reader := range b.done {\n\t\tselect {\n\t\tcase reader <- signal.Signal:\n\t\tdefault:\n\t\t\tunsent++\n\t\t}\n\t}\n\n\treturn len(b.done), unsent\n}\n\nfunc (b *broadcaster) broadcastWait(signal ShutdownSignal) (int, int) {\n\tvar unsent int\n\n\tfor _, reader := range b.wait {\n\t\tselect {\n\t\tcase reader <- signal:\n\t\tdefault:\n\t\t\tunsent++\n\t\t}\n\t}\n\n\treturn len(b.wait), unsent\n}\n\ntype unsentSignalError struct {\n\tSignal ShutdownSignal\n\tUnsent int\n\tTotal  int\n}\n\nfunc (err *unsentSignalError) Error() string {\n\treturn fmt.Sprintf(\n\t\t\"send %v signal: %v/%v channels are blocked\",\n\t\terr.Signal,\n\t\terr.Unsent,\n\t\terr.Total,\n\t)\n}\n"
  },
  {
    "path": "decorate.go",
    "content": "// Copyright (c) 2022 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage fx\n\nimport (\n\t\"fmt\"\n\t\"reflect\"\n\t\"strings\"\n\n\t\"go.uber.org/dig\"\n\t\"go.uber.org/fx/internal/fxreflect\"\n)\n\n// Decorate specifies one or more decorator functions to an Fx application.\n//\n// # Decorator functions\n//\n// Decorator functions let users augment objects in the graph.\n// They can take in zero or more dependencies that must be provided to the\n// application with fx.Provide, and produce one or more values that can be used\n// by other fx.Provide and fx.Invoke calls.\n//\n//\tfx.Decorate(func(log *zap.Logger) *zap.Logger {\n//\t  return log.Named(\"myapp\")\n//\t})\n//\tfx.Invoke(func(log *zap.Logger) {\n//\t  log.Info(\"hello\")\n//\t  // Output:\n//\t  // {\"level\": \"info\",\"logger\":\"myapp\",\"msg\":\"hello\"}\n//\t})\n//\n// The following decorator accepts multiple dependencies from the graph,\n// augments and returns one of them.\n//\n//\tfx.Decorate(func(log *zap.Logger, cfg *Config) *zap.Logger {\n//\t  return log.Named(cfg.Name)\n//\t})\n//\n// Similar to fx.Provide, functions passed to fx.Decorate may optionally return\n// an error as their last result.\n// If a decorator returns a non-nil error, it will halt application startup.\n//\n//\tfx.Decorate(func(conn *sql.DB, cfg *Config) (*sql.DB, error) {\n//\t  if err := conn.Ping(); err != nil {\n//\t    return sql.Open(\"driver-name\", cfg.FallbackDB)\n//\t  }\n//\t  return conn, nil\n//\t})\n//\n// Decorators support both, fx.In and fx.Out structs, similar to fx.Provide and\n// fx.Invoke.\n//\n//\ttype Params struct {\n//\t  fx.In\n//\n//\t  Client usersvc.Client `name:\"readOnly\"`\n//\t}\n//\n//\ttype Result struct {\n//\t  fx.Out\n//\n//\t  Client usersvc.Client `name:\"readOnly\"`\n//\t}\n//\n//\tfx.Decorate(func(p Params) Result {\n//\t  ...\n//\t})\n//\n// Decorators can be annotated with the fx.Annotate function, but not with the\n// fx.Annotated type. Refer to documentation on fx.Annotate() to learn how to\n// use it for annotating functions.\n//\n//\tfx.Decorate(\n//\t  fx.Annotate(\n//\t    func(client usersvc.Client) usersvc.Client {\n//\t      // ...\n//\t    },\n//\t    fx.ParamTags(`name:\"readOnly\"`),\n//\t    fx.ResultTags(`name:\"readOnly\"`),\n//\t  ),\n//\t)\n//\n// Decorators support augmenting, filtering, or replacing value groups.\n// To decorate a value group, expect the entire value group slice and produce\n// the new slice.\n//\n//\ttype HandlerParam struct {\n//\t  fx.In\n//\n//\t  Log      *zap.Logger\n//\t  Handlers []Handler `group:\"server\"\n//\t}\n//\n//\ttype HandlerResult struct {\n//\t  fx.Out\n//\n//\t  Handlers []Handler `group:\"server\"\n//\t}\n//\n//\tfx.Decorate(func(p HandlerParam) HandlerResult {\n//\t  var r HandlerResult\n//\t  for _, handler := range p.Handlers {\n//\t    r.Handlers = append(r.Handlers, wrapWithLogger(p.Log, handler))\n//\t  }\n//\t  return r\n//\t}),\n//\n// Decorators can not add new values to the graph,\n// only modify or replace existing ones.\n// Types returned by a decorator that are not already in the graph\n// will be ignored.\n//\n// # Decorator scope\n//\n// Modifications made to the Fx graph with fx.Decorate are scoped to the\n// deepest fx.Module inside which the decorator was specified.\n//\n//\tfx.Module(\"mymodule\",\n//\t  fx.Decorate(func(log *zap.Logger) *zap.Logger {\n//\t    return log.Named(\"myapp\")\n//\t  }),\n//\t  fx.Invoke(func(log *zap.Logger) {\n//\t    log.Info(\"decorated logger\")\n//\t    // Output:\n//\t    // {\"level\": \"info\",\"logger\":\"myapp\",\"msg\":\"decorated logger\"}\n//\t  }),\n//\t),\n//\tfx.Invoke(func(log *zap.Logger) {\n//\t  log.Info(\"plain logger\")\n//\t  // Output:\n//\t  // {\"level\": \"info\",\"msg\":\"plain logger\"}\n//\t}),\n//\n// Decorations specified in the top-level fx.New call apply across the\n// application and chain with module-specific decorators.\n//\n//\tfx.New(\n//\t  // ...\n//\t  fx.Decorate(func(log *zap.Logger) *zap.Logger {\n//\t    return log.With(zap.Field(\"service\", \"myservice\"))\n//\t  }),\n//\t  // ...\n//\t  fx.Invoke(func(log *zap.Logger) {\n//\t    log.Info(\"outer decorator\")\n//\t    // Output:\n//\t    // {\"level\": \"info\",\"service\":\"myservice\",\"msg\":\"outer decorator\"}\n//\t  }),\n//\t  // ...\n//\t  fx.Module(\"mymodule\",\n//\t    fx.Decorate(func(log *zap.Logger) *zap.Logger {\n//\t      return log.Named(\"myapp\")\n//\t    }),\n//\t    fx.Invoke(func(log *zap.Logger) {\n//\t      log.Info(\"inner decorator\")\n//\t      // Output:\n//\t      // {\"level\": \"info\",\"logger\":\"myapp\",\"service\":\"myservice\",\"msg\":\"inner decorator\"}\n//\t    }),\n//\t  ),\n//\t)\nfunc Decorate(decorators ...any) Option {\n\treturn decorateOption{\n\t\tTargets: decorators,\n\t\tStack:   fxreflect.CallerStack(1, 0),\n\t}\n}\n\ntype decorateOption struct {\n\tTargets []any\n\tStack   fxreflect.Stack\n}\n\nfunc (o decorateOption) apply(mod *module) {\n\tfor _, target := range o.Targets {\n\t\tmod.decorators = append(mod.decorators, decorator{\n\t\t\tTarget: target,\n\t\t\tStack:  o.Stack,\n\t\t})\n\t}\n}\n\nfunc (o decorateOption) String() string {\n\titems := make([]string, len(o.Targets))\n\tfor i, f := range o.Targets {\n\t\titems[i] = fxreflect.FuncName(f)\n\t}\n\treturn fmt.Sprintf(\"fx.Decorate(%s)\", strings.Join(items, \", \"))\n}\n\n// decorator is a single decorator used in Fx.\ntype decorator struct {\n\t// Decorator provided to Fx.\n\tTarget any\n\n\t// Stack trace of where this provide was made.\n\tStack fxreflect.Stack\n\n\t// Whether this decorator was specified via fx.Replace\n\tIsReplace   bool\n\tReplaceType reflect.Type // set only if IsReplace\n}\n\nfunc runDecorator(c container, d decorator, opts ...dig.DecorateOption) (err error) {\n\tdecorator := d.Target\n\tdefer func() {\n\t\tif err != nil {\n\t\t\terr = fmt.Errorf(\"fx.Decorate(%v) from:\\n%+vFailed: %w\", decorator, d.Stack, err)\n\t\t}\n\t}()\n\n\tswitch decorator := decorator.(type) {\n\tcase annotated:\n\t\tif dcor, derr := decorator.Build(); derr == nil {\n\t\t\terr = c.Decorate(dcor, opts...)\n\t\t}\n\tdefault:\n\t\terr = c.Decorate(decorator, opts...)\n\t}\n\treturn\n}\n"
  },
  {
    "path": "decorate_test.go",
    "content": "// Copyright (c) 2022 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage fx_test\n\nimport (\n\t\"errors\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\t\"go.uber.org/fx\"\n\t\"go.uber.org/fx/fxtest\"\n)\n\nfunc TestDecorateSuccess(t *testing.T) {\n\ttype Logger struct {\n\t\tName string\n\t}\n\n\tt.Run(\"objects provided by other modules are decorated\", func(t *testing.T) {\n\t\tredis := fx.Module(\"redis\",\n\t\t\tfx.Provide(func() *Logger {\n\t\t\t\treturn &Logger{Name: \"redis\"}\n\t\t\t}),\n\t\t)\n\n\t\ttestRedis := fx.Module(\"testRedis\",\n\t\t\tredis,\n\t\t\tfx.Decorate(func() *Logger {\n\t\t\t\treturn &Logger{Name: \"testRedis\"}\n\t\t\t}),\n\t\t\tfx.Invoke(func(l *Logger) {\n\t\t\t\tassert.Equal(t, \"testRedis\", l.Name)\n\t\t\t}),\n\t\t)\n\n\t\tapp := fxtest.New(t,\n\t\t\ttestRedis,\n\t\t\tfx.Invoke(func(l *Logger) {\n\t\t\t\tassert.Equal(t, \"redis\", l.Name)\n\t\t\t}),\n\t\t)\n\t\tdefer app.RequireStart().RequireStop()\n\t})\n\n\tt.Run(\"objects in child modules are decorated.\", func(t *testing.T) {\n\t\tredis := fx.Module(\"redis\",\n\t\t\tfx.Decorate(func() *Logger {\n\t\t\t\treturn &Logger{Name: \"redis\"}\n\t\t\t}),\n\t\t\tfx.Invoke(func(l *Logger) {\n\t\t\t\tassert.Equal(t, \"redis\", l.Name)\n\t\t\t}),\n\t\t)\n\t\tapp := fxtest.New(t,\n\t\t\tredis,\n\t\t\tfx.Provide(func() *Logger {\n\t\t\t\tassert.Fail(t, \"should not run this\")\n\t\t\t\treturn &Logger{Name: \"root\"}\n\t\t\t}),\n\t\t)\n\t\tdefer app.RequireStart().RequireStop()\n\t})\n\n\tt.Run(\"root decoration applies to all modules\", func(t *testing.T) {\n\t\tredis := fx.Module(\"redis\",\n\t\t\tfx.Invoke(func(l *Logger) {\n\t\t\t\tassert.Equal(t, \"decorated logger\", l.Name)\n\t\t\t}),\n\t\t)\n\t\tlogger := fx.Module(\"logger\",\n\t\t\tfx.Provide(func() *Logger {\n\t\t\t\treturn &Logger{Name: \"logger\"}\n\t\t\t}),\n\t\t)\n\t\tapp := fxtest.New(t,\n\t\t\tredis,\n\t\t\tlogger,\n\t\t\tfx.Decorate(func(l *Logger) *Logger {\n\t\t\t\treturn &Logger{Name: \"decorated \" + l.Name}\n\t\t\t}),\n\t\t)\n\t\tdefer app.RequireStart().RequireStop()\n\t})\n\n\tt.Run(\"use Decorate with Annotate\", func(t *testing.T) {\n\t\ttype Coffee struct {\n\t\t\tName  string\n\t\t\tPrice int\n\t\t}\n\n\t\tcafe := fx.Module(\"cafe\",\n\t\t\tfx.Provide(fx.Annotate(func() *Coffee {\n\t\t\t\treturn &Coffee{Name: \"Americano\", Price: 3}\n\t\t\t}, fx.ResultTags(`group:\"coffee\"`))),\n\t\t\tfx.Provide(fx.Annotate(func() *Coffee {\n\t\t\t\treturn &Coffee{Name: \"Cappucino\", Price: 4}\n\t\t\t}, fx.ResultTags(`group:\"coffee\"`))),\n\t\t\tfx.Provide(fx.Annotate(func() *Coffee {\n\t\t\t\treturn &Coffee{Name: \"Cold Brew\", Price: 4}\n\t\t\t}, fx.ResultTags(`group:\"coffee\"`))),\n\t\t)\n\n\t\ttakeout := fx.Module(\"takeout\",\n\t\t\tcafe,\n\t\t\tfx.Decorate(fx.Annotate(func(coffee []*Coffee) []*Coffee {\n\t\t\t\tvar newC []*Coffee\n\t\t\t\tfor _, c := range coffee {\n\t\t\t\t\tnewC = append(newC, &Coffee{\n\t\t\t\t\t\tName:  c.Name,\n\t\t\t\t\t\tPrice: c.Price + 1,\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t\treturn newC\n\t\t\t}, fx.ParamTags(`group:\"coffee\"`), fx.ResultTags(`group:\"coffee\"`))),\n\t\t\tfx.Invoke(fx.Annotate(func(coffee []*Coffee) {\n\t\t\t\tassert.Equal(t, 3, len(coffee))\n\t\t\t\ttotalPrice := 0\n\t\t\t\tfor _, c := range coffee {\n\t\t\t\t\ttotalPrice += c.Price\n\t\t\t\t}\n\t\t\t\tassert.Equal(t, 4+5+5, totalPrice)\n\t\t\t}, fx.ParamTags(`group:\"coffee\"`))),\n\t\t)\n\n\t\tapp := fxtest.New(t,\n\t\t\ttakeout,\n\t\t)\n\t\tdefer app.RequireStart().RequireStop()\n\t})\n\n\tt.Run(\"use Decorate with parameter/result struct\", func(t *testing.T) {\n\t\ttype Logger struct {\n\t\t\tName string\n\t\t}\n\t\ttype A struct {\n\t\t\tfx.In\n\n\t\t\tLog     *Logger\n\t\t\tVersion int `name:\"versionNum\"`\n\t\t}\n\t\ttype B struct {\n\t\t\tfx.Out\n\n\t\t\tLog     *Logger\n\t\t\tVersion int `name:\"versionNum\"`\n\t\t}\n\t\tapp := fxtest.New(t,\n\t\t\tfx.Provide(\n\t\t\t\tfx.Annotate(func() int { return 1 },\n\t\t\t\t\tfx.ResultTags(`name:\"versionNum\"`)),\n\t\t\t\tfunc() *Logger {\n\t\t\t\t\treturn &Logger{Name: \"logger\"}\n\t\t\t\t},\n\t\t\t),\n\t\t\tfx.Decorate(func(a A) B {\n\t\t\t\treturn B{\n\t\t\t\t\tLog:     &Logger{Name: a.Log.Name + \" decorated\"},\n\t\t\t\t\tVersion: a.Version + 1,\n\t\t\t\t}\n\t\t\t}),\n\t\t\tfx.Invoke(fx.Annotate(func(l *Logger, ver int) {\n\t\t\t\tassert.Equal(t, \"logger decorated\", l.Name)\n\t\t\t\tassert.Equal(t, 2, ver)\n\t\t\t}, fx.ParamTags(``, `name:\"versionNum\"`))),\n\t\t)\n\t\tdefer app.RequireStart().RequireStop()\n\t})\n\n\tt.Run(\"decorator with soft value group\", func(t *testing.T) {\n\t\tapp := fxtest.New(t,\n\t\t\tfx.Provide(\n\t\t\t\tfx.Annotate(\n\t\t\t\t\tfunc() (string, int) { return \"cheeseburger\", 15 },\n\t\t\t\t\tfx.ResultTags(`group:\"burger\"`, `group:\"potato\"`),\n\t\t\t\t),\n\t\t\t),\n\t\t\tfx.Provide(\n\t\t\t\tfx.Annotate(\n\t\t\t\t\tfunc() (string, int) { return \"mushroomburger\", 35 },\n\t\t\t\t\tfx.ResultTags(`group:\"burger\"`, `group:\"potato\"`),\n\t\t\t\t),\n\t\t\t),\n\t\t\tfx.Provide(\n\t\t\t\tfx.Annotate(\n\t\t\t\t\tfunc() string {\n\t\t\t\t\t\trequire.FailNow(t, \"should not be called\")\n\t\t\t\t\t\treturn \"veggieburger\"\n\t\t\t\t\t},\n\t\t\t\t\tfx.ResultTags(`group:\"burger\"`, `group:\"potato\"`),\n\t\t\t\t),\n\t\t\t),\n\t\t\tfx.Decorate(\n\t\t\t\tfx.Annotate(\n\t\t\t\t\tfunc(burgers []string) []string {\n\t\t\t\t\t\tretBurg := make([]string, len(burgers))\n\t\t\t\t\t\tfor i, burger := range burgers {\n\t\t\t\t\t\t\tretBurg[i] = strings.ToUpper(burger)\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn retBurg\n\t\t\t\t\t},\n\t\t\t\t\tfx.ParamTags(`group:\"burger,soft\"`),\n\t\t\t\t\tfx.ResultTags(`group:\"burger\"`),\n\t\t\t\t),\n\t\t\t),\n\t\t\tfx.Invoke(\n\t\t\t\tfx.Annotate(\n\t\t\t\t\tfunc(burgers []string, fries []int) {\n\t\t\t\t\t\tassert.ElementsMatch(t, []string{\"CHEESEBURGER\", \"MUSHROOMBURGER\"}, burgers)\n\t\t\t\t\t},\n\t\t\t\t\tfx.ParamTags(`group:\"burger,soft\"`, `group:\"potato\"`),\n\t\t\t\t),\n\t\t\t),\n\t\t)\n\t\tdefer app.RequireStart().RequireStop()\n\t\trequire.NoError(t, app.Err())\n\t})\n\n\tt.Run(\"decorator with optional parameter\", func(t *testing.T) {\n\t\ttype Config struct {\n\t\t\tName string\n\t\t}\n\t\ttype Logger struct {\n\t\t\tName string\n\t\t}\n\t\ttype DecoratorParam struct {\n\t\t\tfx.In\n\n\t\t\tCfg *Config `optional:\"true\"`\n\t\t\tLog *Logger\n\t\t}\n\n\t\tapp := fxtest.New(t,\n\t\t\tfx.Provide(func() *Logger { return &Logger{Name: \"log\"} }),\n\t\t\tfx.Decorate(func(p DecoratorParam) *Logger {\n\t\t\t\tif p.Cfg != nil {\n\t\t\t\t\treturn &Logger{Name: p.Cfg.Name}\n\t\t\t\t}\n\t\t\t\treturn &Logger{Name: p.Log.Name}\n\t\t\t}),\n\t\t\tfx.Invoke(func(l *Logger) {\n\t\t\t\tassert.Equal(t, l.Name, \"log\")\n\t\t\t}),\n\t\t)\n\t\tdefer app.RequireStart().RequireStop()\n\t})\n\n\tt.Run(\"transitive decoration\", func(t *testing.T) {\n\t\ttype Config struct {\n\t\t\tScope string\n\t\t}\n\t\ttype Logger struct {\n\t\t\tCfg *Config\n\t\t}\n\t\tapp := fxtest.New(t,\n\t\t\tfx.Provide(func() *Config { return &Config{Scope: \"root\"} }),\n\t\t\tfx.Module(\"child\",\n\t\t\t\tfx.Decorate(func() *Config { return &Config{Scope: \"child\"} }),\n\t\t\t\tfx.Provide(func(cfg *Config) *Logger { return &Logger{Cfg: cfg} }),\n\t\t\t\tfx.Invoke(func(l *Logger) {\n\t\t\t\t\tassert.Equal(t, \"child\", l.Cfg.Scope)\n\t\t\t\t}),\n\t\t\t),\n\t\t)\n\t\tdefer app.RequireStart().RequireStop()\n\t})\n\n\tt.Run(\"ineffective transitive decoration\", func(t *testing.T) {\n\t\ttype Config struct {\n\t\t\tScope string\n\t\t}\n\t\ttype Logger struct {\n\t\t\tCfg *Config\n\t\t}\n\t\tapp := fxtest.New(t,\n\t\t\tfx.Provide(func() *Config {\n\t\t\t\treturn &Config{Scope: \"root\"}\n\t\t\t}),\n\t\t\tfx.Provide(func(cfg *Config) *Logger {\n\t\t\t\treturn &Logger{Cfg: &Config{\n\t\t\t\t\tScope: cfg.Scope + \" logger\",\n\t\t\t\t}}\n\t\t\t}),\n\t\t\tfx.Module(\"child\",\n\t\t\t\tfx.Decorate(func() *Config {\n\t\t\t\t\treturn &Config{Scope: \"child\"}\n\t\t\t\t}),\n\t\t\t\t// Logger does not get replaced since it was provided\n\t\t\t\t// from a different Scope.\n\t\t\t\tfx.Invoke(func(l *Logger) {\n\t\t\t\t\tassert.Equal(t, \"root logger\", l.Cfg.Scope)\n\t\t\t\t}),\n\t\t\t),\n\t\t)\n\t\tdefer app.RequireStart().RequireStop()\n\t})\n\n\tt.Run(\"decoration must execute when required by a member of group\", func(t *testing.T) {\n\t\ttype Drinks any\n\t\ttype Coffee struct {\n\t\t\tType  string\n\t\t\tName  string\n\t\t\tPrice int\n\t\t}\n\t\ttype PriceService struct {\n\t\t\tDefaultPrice int\n\t\t}\n\t\tapp := fxtest.New(t,\n\t\t\tfx.Provide(func() *PriceService {\n\t\t\t\treturn &PriceService{DefaultPrice: 3}\n\t\t\t}),\n\t\t\tfx.Decorate(func(service *PriceService) *PriceService {\n\t\t\t\tservice.DefaultPrice = 10\n\t\t\t\treturn service\n\t\t\t}),\n\t\t\tfx.Provide(fx.Annotate(func(service *PriceService) Drinks {\n\t\t\t\tassert.Equal(t, 10, service.DefaultPrice)\n\t\t\t\treturn &Coffee{Type: \"coffee\", Name: \"Americano\", Price: service.DefaultPrice}\n\t\t\t}, fx.ResultTags(`group:\"drinks\"`))),\n\t\t\tfx.Provide(fx.Annotated{Group: \"drinks\", Target: func() Drinks {\n\t\t\t\treturn &Coffee{Type: \"coffee\", Name: \"Cold Brew\", Price: 4}\n\t\t\t}}),\n\t\t\tfx.Invoke(fx.Annotate(func(drinks []Drinks) {\n\t\t\t\tassert.Len(t, drinks, 2)\n\t\t\t}, fx.ParamTags(`group:\"drinks\"`))),\n\t\t)\n\t\tdefer app.RequireStart().RequireStop()\n\t})\n}\n\nfunc TestDecorateFailure(t *testing.T) {\n\tt.Run(\"decorator returns an error\", func(t *testing.T) {\n\t\ttype Logger struct {\n\t\t\tName string\n\t\t}\n\n\t\tapp := NewForTest(t,\n\t\t\tfx.Provide(func() *Logger {\n\t\t\t\treturn &Logger{Name: \"root\"}\n\t\t\t}),\n\t\t\tfx.Decorate(func(l *Logger) (*Logger, error) {\n\t\t\t\treturn &Logger{Name: l.Name + \"decorated\"}, errors.New(\"minor sadness\")\n\t\t\t}),\n\t\t\tfx.Invoke(func(l *Logger) {\n\t\t\t\tassert.Fail(t, \"this should not be executed\")\n\t\t\t}),\n\t\t)\n\n\t\terr := app.Err()\n\t\trequire.Error(t, err)\n\t\tassert.Contains(t, err.Error(), \"minor sadness\")\n\t})\n\n\tt.Run(\"decorator in a nested module returns an error\", func(t *testing.T) {\n\t\ttype Logger struct {\n\t\t\tName string\n\t\t}\n\n\t\tapp := NewForTest(t,\n\t\t\tfx.Provide(func() *Logger {\n\t\t\t\treturn &Logger{Name: \"root\"}\n\t\t\t}),\n\t\t\tfx.Module(\"child\",\n\t\t\t\tfx.Decorate(func(l *Logger) *Logger {\n\t\t\t\t\treturn &Logger{Name: l.Name + \"decorated\"}\n\t\t\t\t}),\n\t\t\t\tfx.Decorate(func(l *Logger) *Logger {\n\t\t\t\t\treturn &Logger{Name: l.Name + \"decorated\"}\n\t\t\t\t}),\n\t\t\t\tfx.Invoke(func(l *Logger) {\n\t\t\t\t\tassert.Fail(t, \"this should not be executed\")\n\t\t\t\t}),\n\t\t\t),\n\t\t)\n\n\t\terr := app.Err()\n\t\trequire.Error(t, err)\n\t\tassert.Contains(t, err.Error(), \"*fx_test.Logger already decorated\")\n\t})\n\n\tt.Run(\"decorating a type more than once in the same Module errors\", func(t *testing.T) {\n\t\ttype Logger struct {\n\t\t\tName string\n\t\t}\n\n\t\tapp := NewForTest(t,\n\t\t\tfx.Provide(func() *Logger {\n\t\t\t\treturn &Logger{Name: \"root\"}\n\t\t\t}),\n\t\t\tfx.Decorate(func(l *Logger) *Logger {\n\t\t\t\treturn &Logger{Name: \"dec1 \" + l.Name}\n\t\t\t}),\n\t\t\tfx.Decorate(func(l *Logger) *Logger {\n\t\t\t\treturn &Logger{Name: \"dec2 \" + l.Name}\n\t\t\t}),\n\t\t)\n\n\t\terr := app.Err()\n\t\trequire.Error(t, err)\n\t\tassert.Contains(t, err.Error(), \"*fx_test.Logger already decorated\")\n\t})\n\n\tt.Run(\"annotated decorator returns an error\", func(t *testing.T) {\n\t\ttype Logger struct {\n\t\t\tName string\n\t\t}\n\n\t\ttag := `name:\"decoratedLogger\"`\n\t\tapp := NewForTest(t,\n\t\t\tfx.Provide(fx.Annotate(func() *Logger {\n\t\t\t\treturn &Logger{Name: \"root\"}\n\t\t\t}, fx.ResultTags(tag))),\n\t\t\tfx.Decorate(fx.Annotate(func(l *Logger) (*Logger, error) {\n\t\t\t\treturn &Logger{Name: \"dec1 \" + l.Name}, errors.New(\"major sadness\")\n\t\t\t}, fx.ParamTags(tag), fx.ResultTags(tag))),\n\t\t\tfx.Invoke(fx.Annotate(func(l *Logger) {\n\t\t\t\tassert.Fail(t, \"this should never run\")\n\t\t\t}, fx.ParamTags(tag))),\n\t\t)\n\n\t\terr := app.Err()\n\t\trequire.Error(t, err)\n\t\tassert.Contains(t, err.Error(), \"major sadness\")\n\t})\n\n\tt.Run(\"all decorator dependencies must be provided\", func(t *testing.T) {\n\t\ttype Logger struct {\n\t\t\tName string\n\t\t}\n\t\ttype Config struct {\n\t\t\tName string\n\t\t}\n\n\t\tapp := NewForTest(t,\n\t\t\tfx.Provide(func() *Logger {\n\t\t\t\treturn &Logger{Name: \"logger\"}\n\t\t\t}),\n\t\t\tfx.Decorate(func(l *Logger, c *Config) *Logger {\n\t\t\t\treturn &Logger{Name: l.Name + c.Name}\n\t\t\t}),\n\t\t\tfx.Invoke(func(l *Logger) {\n\t\t\t\tassert.Fail(t, \"this should never run\")\n\t\t\t}),\n\t\t)\n\n\t\terr := app.Err()\n\t\trequire.Error(t, err)\n\t\tassert.Contains(t, err.Error(), \"missing dependencies\")\n\t})\n\n\tt.Run(\"decorate cannot provide a non-existent type\", func(t *testing.T) {\n\t\ttype Logger struct {\n\t\t\tName string\n\t\t}\n\n\t\tapp := NewForTest(t,\n\t\t\tfx.Decorate(func() *Logger {\n\t\t\t\treturn &Logger{Name: \"decorator\"}\n\t\t\t}),\n\t\t\tfx.Invoke(func(l *Logger) {\n\t\t\t\tassert.Fail(t, \"this should never run\")\n\t\t\t}),\n\t\t)\n\n\t\terr := app.Err()\n\t\trequire.Error(t, err)\n\t\tassert.Contains(t, err.Error(), \"missing dependencies\")\n\t})\n}\n"
  },
  {
    "path": "doc.go",
    "content": "// Copyright (c) 2019 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\n// Package fx is a framework that makes it easy to build applications out of\n// reusable, composable modules.\n//\n// Fx applications use dependency injection to eliminate globals without the\n// tedium of manually wiring together function calls. Unlike other approaches\n// to dependency injection, Fx works with plain Go functions: you don't need\n// to use struct tags or embed special types, so Fx automatically works well\n// with most Go packages.\n//\n// # Basic usage\n//\n// Basic usage is explained in the package-level example.\n// If you're new to Fx, start there!\n//\n// Advanced features, including named instances, optional parameters,\n// and value groups, are explained in this section further down.\n//\n// # Testing Fx Applications\n//\n// To test functions that use the Lifecycle type or to write end-to-end tests\n// of your Fx application, use the helper functions and types provided by the\n// go.uber.org/fx/fxtest package.\n//\n// # Parameter Structs\n//\n// Fx constructors declare their dependencies as function parameters. This can\n// quickly become unreadable if the constructor has a lot of dependencies.\n//\n//\tfunc NewHandler(users *UserGateway, comments *CommentGateway, posts *PostGateway, votes *VoteGateway, authz *AuthZGateway) *Handler {\n//\t\t// ...\n//\t}\n//\n// To improve the readability of constructors like this, create a struct that\n// lists all the dependencies as fields and change the function to accept that\n// struct instead. The new struct is called a parameter struct.\n//\n// Fx has first class support for parameter structs: any struct embedding\n// fx.In gets treated as a parameter struct, so the individual fields in the\n// struct are supplied via dependency injection. Using a parameter struct, we\n// can make the constructor above much more readable:\n//\n//\ttype HandlerParams struct {\n//\t\tfx.In\n//\n//\t\tUsers    *UserGateway\n//\t\tComments *CommentGateway\n//\t\tPosts    *PostGateway\n//\t\tVotes    *VoteGateway\n//\t\tAuthZ    *AuthZGateway\n//\t}\n//\n//\tfunc NewHandler(p HandlerParams) *Handler {\n//\t\t// ...\n//\t}\n//\n// Though it's rarely necessary to mix the two, constructors can receive any\n// combination of parameter structs and parameters.\n//\n//\tfunc NewHandler(p HandlerParams, l *log.Logger) *Handler {\n//\t\t// ...\n//\t}\n//\n// # Result Structs\n//\n// Result structs are the inverse of parameter structs.\n// These structs represent multiple outputs from a\n// single function as fields. Fx treats all structs embedding fx.Out as result\n// structs, so other constructors can rely on the result struct's fields\n// directly.\n//\n// Without result structs, we sometimes have function definitions like this:\n//\n//\tfunc SetupGateways(conn *sql.DB) (*UserGateway, *CommentGateway, *PostGateway, error) {\n//\t\t// ...\n//\t}\n//\n// With result structs, we can make this both more readable and easier to\n// modify in the future:\n//\n//\ttype Gateways struct {\n//\t\tfx.Out\n//\n//\t\tUsers    *UserGateway\n//\t\tComments *CommentGateway\n//\t\tPosts    *PostGateway\n//\t}\n//\n//\tfunc SetupGateways(conn *sql.DB) (Gateways, error) {\n//\t\t// ...\n//\t}\n//\n// # Named Values\n//\n// Some use cases require the application container to hold multiple values of\n// the same type.\n//\n// A constructor that produces a result struct can tag any field with\n// `name:\"..\"` to have the corresponding value added to the graph under the\n// specified name. An application may contain at most one unnamed value of a\n// given type, but may contain any number of named values of the same type.\n//\n//\ttype ConnectionResult struct {\n//\t\tfx.Out\n//\n//\t\tReadWrite *sql.DB `name:\"rw\"`\n//\t\tReadOnly  *sql.DB `name:\"ro\"`\n//\t}\n//\n//\tfunc ConnectToDatabase(...) (ConnectionResult, error) {\n//\t\t// ...\n//\t\treturn ConnectionResult{ReadWrite: rw, ReadOnly:  ro}, nil\n//\t}\n//\n// Similarly, a constructor that accepts a parameter struct can tag any field\n// with `name:\"..\"` to have the corresponding value injected by name.\n//\n//\ttype GatewayParams struct {\n//\t\tfx.In\n//\n//\t\tWriteToConn  *sql.DB `name:\"rw\"`\n//\t\tReadFromConn *sql.DB `name:\"ro\"`\n//\t}\n//\n//\tfunc NewCommentGateway(p GatewayParams) (*CommentGateway, error) {\n//\t\t// ...\n//\t}\n//\n// Note that both the name AND type of the fields on the\n// parameter struct must match the corresponding result struct.\n//\n// # Optional Dependencies\n//\n// Constructors often have optional dependencies on some types: if those types are\n// missing, they can operate in a degraded state. Fx supports optional\n// dependencies via the `optional:\"true\"` tag to fields on parameter structs.\n//\n//\ttype UserGatewayParams struct {\n//\t\tfx.In\n//\n//\t\tConn  *sql.DB\n//\t\tCache *redis.Client `optional:\"true\"`\n//\t}\n//\n// If an optional field isn't available in the container, the constructor\n// receives the field's zero value.\n//\n//\tfunc NewUserGateway(p UserGatewayParams, log *log.Logger) (*UserGateway, error) {\n//\t\tif p.Cache == nil {\n//\t\t\tlog.Print(\"Caching disabled\")\n//\t\t}\n//\t\t// ...\n//\t}\n//\n// Constructors that declare optional dependencies MUST gracefully handle\n// situations in which those dependencies are absent.\n//\n// The optional tag also allows adding new dependencies without breaking\n// existing consumers of the constructor.\n//\n// The optional tag may be combined with the name tag to declare a named\n// value dependency optional.\n//\n//\ttype GatewayParams struct {\n//\t\tfx.In\n//\n//\t\tWriteToConn  *sql.DB `name:\"rw\"`\n//\t\tReadFromConn *sql.DB `name:\"ro\" optional:\"true\"`\n//\t}\n//\n//\tfunc NewCommentGateway(p GatewayParams, log *log.Logger) (*CommentGateway, error) {\n//\t\tif p.ReadFromConn == nil {\n//\t\t\tlog.Print(\"Warning: Using RW connection for reads\")\n//\t\t\tp.ReadFromConn = p.WriteToConn\n//\t\t}\n//\t\t// ...\n//\t}\n//\n// # Value Groups\n//\n// To make it easier to produce and consume many values of the same type, Fx\n// supports named, unordered collections called value groups.\n//\n// Constructors can send values into value groups by returning a result struct\n// tagged with `group:\"..\"`.\n//\n//\ttype HandlerResult struct {\n//\t\tfx.Out\n//\n//\t\tHandler Handler `group:\"server\"`\n//\t}\n//\n//\tfunc NewHelloHandler() HandlerResult {\n//\t\t// ...\n//\t}\n//\n//\tfunc NewEchoHandler() HandlerResult {\n//\t\t// ...\n//\t}\n//\n// Any number of constructors may provide values to this named collection, but\n// the ordering of the final collection is unspecified.\n//\n// Value groups require parameter and result structs to use fields with\n// different types: if a group of constructors each returns type T, parameter\n// structs consuming the group must use a field of type []T.\n//\n// Parameter structs can request a value group by using a field of type []T\n// tagged with `group:\"..\"`.\n// This will execute all constructors that provide a value to\n// that group in an unspecified order, then collect all the results into a\n// single slice.\n//\n//\ttype ServerParams struct {\n//\t\tfx.In\n//\n//\t\tHandlers []Handler `group:\"server\"`\n//\t}\n//\n//\tfunc NewServer(p ServerParams) *Server {\n//\t\tserver := newServer()\n//\t\tfor _, h := range p.Handlers {\n//\t\t\tserver.Register(h)\n//\t\t}\n//\t\treturn server\n//\t}\n//\n// Note that values in a value group are unordered. Fx makes no guarantees\n// about the order in which these values will be produced.\n//\n// # Soft Value Groups\n//\n// By default, when a constructor declares a dependency on a value group,\n// all values provided to that value group are eagerly instantiated.\n// That is undesirable for cases where an optional component wants to\n// contribute to a value group, but only if it was actually used\n// by the rest of the application.\n//\n// A soft value group can be thought of as a best-attempt at populating the\n// group with values from constructors that have already run. In other words,\n// if a constructor's output type is only consumed by a soft value group,\n// it will not be run.\n//\n// Note that Fx randomizes the order of values in the value group,\n// so the slice of values may not match the order in which constructors\n// were run.\n//\n// To declare a soft relationship between a group and its constructors, use\n// the `soft` option on the input group tag (`group:\"[groupname],soft\"`).\n// This option is only valid for input parameters.\n//\n//\ttype Params struct {\n//\t\tfx.In\n//\n//\t\tHandlers []Handler `group:\"server,soft\"`\n//\t\tLogger   *zap.Logger\n//\t}\n//\n//\tfunc NewServer(p Params) *Server {\n//\t\t// ...\n//\t}\n//\n// With such a declaration, a constructor that provides a value to the 'server'\n// value group will be called only if there's another instantiated component\n// that consumes the results of that constructor.\n//\n//\tfunc NewHandlerAndLogger() (Handler, *zap.Logger) {\n//\t\t// ...\n//\t}\n//\n//\tfunc NewHandler() Handler {\n//\t\t// ...\n//\t}\n//\n//\tfx.Provide(\n//\t\tfx.Annotate(NewHandlerAndLogger, fx.ResultTags(`group:\"server\"`)),\n//\t\tfx.Annotate(NewHandler, fx.ResultTags(`group:\"server\"`)),\n//\t)\n//\n// NewHandlerAndLogger will be called because the Logger is consumed by the\n// application, but NewHandler will not be called because it's only consumed\n// by the soft value group.\n//\n// # Value group flattening\n//\n// By default, values of type T produced to a value group are consumed as []T.\n//\n//\ttype HandlerResult struct {\n//\t\tfx.Out\n//\n//\t\tHandler Handler `group:\"server\"`\n//\t}\n//\n//\ttype ServerParams struct {\n//\t\tfx.In\n//\n//\t\tHandlers []Handler `group:\"server\"`\n//\t}\n//\n// This means that if the producer produces []T,\n// the consumer must consume [][]T.\n//\n// There are cases where it's desirable\n// for the producer (the fx.Out) to produce multiple values ([]T),\n// and for the consumer (the fx.In) consume them as a single slice ([]T).\n// Fx offers flattened value groups for this purpose.\n//\n// To provide multiple values for a group from a result struct, produce a\n// slice and use the `,flatten` option on the group tag. This indicates that\n// each element in the slice should be injected into the group individually.\n//\n//\ttype HandlerResult struct {\n//\t\tfx.Out\n//\n//\t\tHandler []Handler `group:\"server,flatten\"`\n//\t\t// Consumed as []Handler in ServerParams.\n//\t}\n//\n// # Unexported fields\n//\n// By default, a type that embeds fx.In may not have any unexported fields. The\n// following will return an error if used with Fx.\n//\n//\ttype Params struct {\n//\t\tfx.In\n//\n//\t\tLogger *zap.Logger\n//\t\tmu     sync.Mutex\n//\t}\n//\n// If you have need of unexported fields on such a type, you may opt-into\n// ignoring unexported fields by adding the ignore-unexported struct tag to the\n// fx.In. For example,\n//\n//\ttype Params struct {\n//\t\tfx.In `ignore-unexported:\"true\"`\n//\n//\t\tLogger *zap.Logger\n//\t\tmu     sync.Mutex\n//\t}\npackage fx // import \"go.uber.org/fx\"\n"
  },
  {
    "path": "docs/.gitattributes",
    "content": "# Mark the uv.lock as generated so it is collapsed in review by default.\nuv.lock linguist-generated\n"
  },
  {
    "path": "docs/.gitignore",
    "content": "/_site\n"
  },
  {
    "path": "docs/Makefile",
    "content": ".PHONY: build\nbuild:\n\tuv run mkdocs build\n\n.PHONY: serve\nserve:\n\tuv run mkdocs serve\n"
  },
  {
    "path": "docs/ex/annotate/cast.go",
    "content": "// Copyright (c) 2022 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage annotate\n\nimport (\n\t\"net/http\"\n\n\t\"go.uber.org/fx\"\n\t\"go.uber.org/fx/docs/ex/annotate/github\"\n)\n\n// HTTPClient matches the http.Client interface.\n// --8<-- [start:interface]\ntype HTTPClient interface {\n\tDo(*http.Request) (*http.Response, error)\n}\n\n// This is a compile-time check that verifies\n// that our interface matches the API of http.Client.\nvar _ HTTPClient = (*http.Client)(nil)\n\n// --8<-- [end:interface]\n\n// Config specifies the configuration of a client.\ntype Config struct{}\n\n// NewHTTPClient builds a new HTTP client.\n// --8<-- [start:constructor]\nfunc NewHTTPClient(Config) (*http.Client, error) {\n\t// --8<-- [end:constructor]\n\treturn http.DefaultClient, nil\n}\n\n// NewGitHubClient builds a new GitHub client.\n// --8<-- [start:iface-consumer]\nfunc NewGitHubClient(client HTTPClient) *github.Client {\n\t// --8<-- [end:iface-consumer]\n\treturn new(github.Client)\n}\n\nfunc options() fx.Option {\n\treturn fx.Options(\n\t\t// --8<-- [start:provides]\n\t\tfx.Provide(\n\t\t\tfx.Annotate(\n\t\t\t\tNewHTTPClient,\n\t\t\t\tfx.As(new(HTTPClient)),\n\t\t\t),\n\t\t\tNewGitHubClient,\n\t\t),\n\t\t// --8<-- [end:provides]\n\t)\n}\n"
  },
  {
    "path": "docs/ex/annotate/cast_bad.go",
    "content": "// Copyright (c) 2022 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\n//go:build ignore\n// +build ignore\n\npackage annotate\n\nimport (\n\t\"net/http\"\n\n\t\"go.uber.org/fx\"\n\t\"go.uber.org/fx/docs/ex/annotate/github\"\n)\n\n// NewGitHubClient builds a new GitHub client.\n// --8<-- [start:struct-consumer]\nfunc NewGitHubClient(client *http.Client) *github.Client {\n\t// --8<-- [end:struct-consumer]\n\treturn new(github.Client)\n}\n\nfunc options() fx.Option {\n\treturn fx.Options(\n\t\t// --8<-- [start:provides]\n\t\tfx.Provide(\n\t\t\tNewHTTPClient,\n\t\t\tNewGitHubClient,\n\t\t),\n\t\t// --8<-- [end:provides]\n\t)\n}\n"
  },
  {
    "path": "docs/ex/annotate/cast_test.go",
    "content": "// Copyright (c) 2022 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage annotate\n\nimport (\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"go.uber.org/fx\"\n\t\"go.uber.org/fx/docs/ex/annotate/github\"\n\t\"go.uber.org/fx/fxtest\"\n)\n\nfunc TestCast(t *testing.T) {\n\tvar gh *github.Client\n\tapp := fxtest.New(t,\n\t\tfx.Supply(Config{}),\n\t\toptions(),\n\t\tfx.Populate(&gh),\n\t)\n\tapp.RequireStart().RequireStop()\n\tassert.NotNil(t, gh)\n}\n"
  },
  {
    "path": "docs/ex/annotate/github/stub.go",
    "content": "// Copyright (c) 2022 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage github\n\n// Client is a stub of a GitHub client.\ntype Client struct{}\n"
  },
  {
    "path": "docs/ex/annotate/sample.go",
    "content": "// Copyright (c) 2022 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage annotate\n\nimport (\n\t\"go.uber.org/fx\"\n)\n\nfunc howToAnnotate() (before, after fx.Option) {\n\tbefore = fx.Options(\n\t\t// --8<-- [start:before]\n\t\tfx.Provide(\n\t\t\tNewHTTPClient,\n\t\t),\n\t\t// --8<-- [end:before]\n\t)\n\tafter = fx.Options(\n\t\t// --8<-- [start:wrap-1]\n\t\t// --8<-- [start:annotate]\n\t\tfx.Provide(\n\t\t\tfx.Annotate(\n\t\t\t\tNewHTTPClient,\n\t\t\t\t// --8<-- [end:wrap-1]\n\t\t\t\tfx.ResultTags(`name:\"client\"`),\n\t\t\t\t// --8<-- [start:wrap-2]\n\t\t\t),\n\t\t),\n\t\t// --8<-- [end:annotate]\n\t\t// --8<-- [end:wrap-2]\n\t)\n\treturn before, after\n}\n"
  },
  {
    "path": "docs/ex/annotate/sample_test.go",
    "content": "// Copyright (c) 2022 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage annotate\n\nimport (\n\t\"net/http\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"go.uber.org/fx\"\n\t\"go.uber.org/fx/fxtest\"\n)\n\nfunc TestHowToAnnotate(t *testing.T) {\n\tbefore, after := howToAnnotate()\n\n\tt.Run(\"before\", func(t *testing.T) {\n\t\tvar got *http.Client\n\t\tapp := fxtest.New(t,\n\t\t\tfx.Supply(Config{}),\n\t\t\tbefore,\n\t\t\tfx.Populate(&got),\n\t\t)\n\t\tapp.RequireStart().RequireStop()\n\t\tassert.NotNil(t, got)\n\t})\n\n\tt.Run(\"after\", func(t *testing.T) {\n\t\tvar got struct {\n\t\t\tfx.In\n\n\t\t\tClient *http.Client `name:\"client\"`\n\t\t}\n\t\tapp := fxtest.New(t,\n\t\t\tfx.Supply(Config{}),\n\t\t\tafter,\n\t\t\tfx.Populate(&got),\n\t\t)\n\t\tapp.RequireStart().RequireStop()\n\t\tassert.NotNil(t, got.Client)\n\t})\n}\n"
  },
  {
    "path": "docs/ex/get-started/01-minimal/main.go",
    "content": "// Copyright (c) 2022 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\n// --8<-- [start:main]\n\npackage main\n\nimport \"go.uber.org/fx\"\n\nfunc main() {\n\tfx.New().Run()\n}\n\n// --8<-- [end:main]\n"
  },
  {
    "path": "docs/ex/get-started/01-minimal/main_test.go",
    "content": "// Copyright (c) 2022 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage main\n\nimport (\n\t\"testing\"\n\n\t\"go.uber.org/fx/docs/internal/apptest\"\n)\n\nfunc TestRun(t *testing.T) {\n\tapptest.Start(t, main)\n}\n"
  },
  {
    "path": "docs/ex/get-started/02-http-server/main.go",
    "content": "// Copyright (c) 2022 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"net\"\n\t\"net/http\"\n\n\t\"go.uber.org/fx\"\n)\n\n// --8<-- [start:provide-server-1]\nfunc main() {\n\t// --8<-- [start:app]\n\tfx.New(\n\t\tfx.Provide(NewHTTPServer),\n\t\t// --8<-- [end:provide-server-1]\n\t\tfx.Invoke(func(*http.Server) {}),\n\t// --8<-- [start:provide-server-2]\n\t).Run()\n\t// --8<-- [end:app]\n}\n\n// --8<-- [end:provide-server-2]\n\n// --8<-- [start:partial-1]\n\n// NewHTTPServer builds an HTTP server that will begin serving requests\n// when the Fx application starts.\n// --8<-- [start:full]\nfunc NewHTTPServer(lc fx.Lifecycle) *http.Server {\n\tsrv := &http.Server{Addr: \":8080\"}\n\t// --8<-- [end:partial-1]\n\tlc.Append(fx.Hook{\n\t\tOnStart: func(ctx context.Context) error {\n\t\t\tln, err := net.Listen(\"tcp\", srv.Addr)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tfmt.Println(\"Starting HTTP server at\", srv.Addr)\n\t\t\tgo srv.Serve(ln)\n\t\t\treturn nil\n\t\t},\n\t\tOnStop: func(ctx context.Context) error {\n\t\t\treturn srv.Shutdown(ctx)\n\t\t},\n\t})\n\t// --8<-- [start:partial-2]\n\treturn srv\n}\n\n// --8<-- [end:partial-2]\n// --8<-- [end:full]\n"
  },
  {
    "path": "docs/ex/get-started/02-http-server/main_test.go",
    "content": "// Copyright (c) 2022 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage main\n\nimport (\n\t\"net/http\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\t\"go.uber.org/fx/docs/internal/apptest\"\n)\n\nfunc TestRun(t *testing.T) {\n\tapptest.Start(t, main)\n\n\tres, err := http.Get(\"http://127.0.0.1:8080/hello\")\n\trequire.NoError(t, err)\n\tdefer res.Body.Close()\n\n\tassert.Equal(t, http.StatusNotFound, res.StatusCode)\n}\n"
  },
  {
    "path": "docs/ex/get-started/03-echo-handler/main.go",
    "content": "// Copyright (c) 2022 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"io\"\n\t\"net\"\n\t\"net/http\"\n\t\"os\"\n\n\t\"go.uber.org/fx\"\n)\n\nfunc main() {\n\tfx.New(\n\t\t// --8<-- [start:provides]\n\t\t// --8<-- [start:provide-handler-1]\n\t\tfx.Provide(\n\t\t\tNewHTTPServer,\n\t\t\t// --8<-- [end:provide-handler-1]\n\t\t\tNewServeMux,\n\t\t\t// --8<-- [start:provide-handler-2]\n\t\t\tNewEchoHandler,\n\t\t),\n\t\t// --8<-- [end:provides]\n\t\tfx.Invoke(func(*http.Server) {}),\n\t\t// --8<-- [end:provide-handler-2]\n\t).Run()\n}\n\n// --8<-- [start:serve-mux]\n\n// NewServeMux builds a ServeMux that will route requests\n// to the given EchoHandler.\nfunc NewServeMux(echo *EchoHandler) *http.ServeMux {\n\tmux := http.NewServeMux()\n\tmux.Handle(\"/echo\", echo)\n\treturn mux\n}\n\n// --8<-- [end:serve-mux]\n\n// --8<-- [start:echo-handler]\n\n// EchoHandler is an http.Handler that copies its request body\n// back to the response.\ntype EchoHandler struct{}\n\n// NewEchoHandler builds a new EchoHandler.\nfunc NewEchoHandler() *EchoHandler {\n\treturn &EchoHandler{}\n}\n\n// ServeHTTP handles an HTTP request to the /echo endpoint.\nfunc (*EchoHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {\n\tif _, err := io.Copy(w, r.Body); err != nil {\n\t\tfmt.Fprintln(os.Stderr, \"Failed to handle request:\", err)\n\t}\n}\n\n// --8<-- [end:echo-handler]\n\n// NewHTTPServer builds an HTTP server that will begin serving requests\n// when the Fx application starts.\n// --8<-- [start:connect-mux]\nfunc NewHTTPServer(lc fx.Lifecycle, mux *http.ServeMux) *http.Server {\n\tsrv := &http.Server{Addr: \":8080\", Handler: mux}\n\tlc.Append(fx.Hook{\n\t\t// --8<-- [end:connect-mux]\n\t\tOnStart: func(ctx context.Context) error {\n\t\t\tln, err := net.Listen(\"tcp\", srv.Addr)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tfmt.Println(\"Starting HTTP server at\", srv.Addr)\n\t\t\tgo srv.Serve(ln)\n\t\t\treturn nil\n\t\t},\n\t\tOnStop: func(ctx context.Context) error {\n\t\t\treturn srv.Shutdown(ctx)\n\t\t},\n\t})\n\treturn srv\n}\n"
  },
  {
    "path": "docs/ex/get-started/03-echo-handler/main_test.go",
    "content": "// Copyright (c) 2022 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage main\n\nimport (\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"go.uber.org/fx/docs/internal/apptest\"\n\t\"go.uber.org/fx/docs/internal/httptest\"\n)\n\nfunc TestRun(t *testing.T) {\n\tapptest.Start(t, main)\n\n\tgot := httptest.PostSuccess(t, \"http://127.0.0.1:8080/echo\", \"great success\")\n\tassert.Equal(t, \"great success\", got)\n}\n"
  },
  {
    "path": "docs/ex/get-started/04-logger/main.go",
    "content": "// Copyright (c) 2022 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage main\n\nimport (\n\t\"context\"\n\t\"io\"\n\t\"net\"\n\t\"net/http\"\n\n\t\"go.uber.org/fx\"\n\t\"go.uber.org/fx/fxevent\"\n\t\"go.uber.org/zap\"\n)\n\n// --8<-- [start:fx-logger]\nfunc main() {\n\tfx.New(\n\t\tfx.WithLogger(func(log *zap.Logger) fxevent.Logger {\n\t\t\treturn &fxevent.ZapLogger{Logger: log}\n\t\t}),\n\t\t// --8<-- [end:fx-logger]\n\t\t// --8<-- [start:provides]\n\t\tfx.Provide(\n\t\t\tNewHTTPServer,\n\t\t\tNewServeMux,\n\t\t\tNewEchoHandler,\n\t\t\tzap.NewExample,\n\t\t),\n\t\t// --8<-- [end:provides]\n\t\tfx.Invoke(func(*http.Server) {}),\n\t).Run()\n}\n\n// NewServeMux builds a ServeMux that will route requests\n// to the given EchoHandler.\nfunc NewServeMux(echo *EchoHandler) *http.ServeMux {\n\tmux := http.NewServeMux()\n\tmux.Handle(\"/echo\", echo)\n\treturn mux\n}\n\n// EchoHandler is an http.Handler that copies its request body\n// back to the response.\n// --8<-- [start:echo-init-1]\ntype EchoHandler struct {\n\tlog *zap.Logger\n}\n\n// --8<-- [end:echo-init-1]\n\n// NewEchoHandler builds a new EchoHandler.\n// --8<-- [start:echo-init-2]\nfunc NewEchoHandler(log *zap.Logger) *EchoHandler {\n\treturn &EchoHandler{log: log}\n}\n\n// --8<-- [end:echo-init-2]\n\n// ServeHTTP handles an HTTP request to the /echo endpoint.\n// --8<-- [start:echo-serve]\nfunc (h *EchoHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {\n\tif _, err := io.Copy(w, r.Body); err != nil {\n\t\th.log.Warn(\"Failed to handle request\", zap.Error(err))\n\t}\n}\n\n// --8<-- [end:echo-serve]\n\n// NewHTTPServer builds an HTTP server that will begin serving requests\n// when the Fx application starts.\n// --8<-- [start:http-server]\nfunc NewHTTPServer(lc fx.Lifecycle, mux *http.ServeMux, log *zap.Logger) *http.Server {\n\tsrv := &http.Server{Addr: \":8080\", Handler: mux}\n\tlc.Append(fx.Hook{\n\t\tOnStart: func(ctx context.Context) error {\n\t\t\tln, err := net.Listen(\"tcp\", srv.Addr)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tlog.Info(\"Starting HTTP server\", zap.String(\"addr\", srv.Addr))\n\t\t\tgo srv.Serve(ln)\n\t\t\t// --8<-- [end:http-server]\n\t\t\treturn nil\n\t\t},\n\t\tOnStop: func(ctx context.Context) error {\n\t\t\treturn srv.Shutdown(ctx)\n\t\t},\n\t})\n\treturn srv\n}\n"
  },
  {
    "path": "docs/ex/get-started/04-logger/main_test.go",
    "content": "// Copyright (c) 2022 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage main\n\nimport (\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"go.uber.org/fx/docs/internal/apptest\"\n\t\"go.uber.org/fx/docs/internal/httptest\"\n)\n\nfunc TestRun(t *testing.T) {\n\tapptest.Start(t, main)\n\n\tgot := httptest.PostSuccess(t, \"http://127.0.0.1:8080/echo\", \"great success\")\n\tassert.Equal(t, \"great success\", got)\n}\n"
  },
  {
    "path": "docs/ex/get-started/05-registration/main.go",
    "content": "// Copyright (c) 2022 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage main\n\nimport (\n\t\"context\"\n\t\"io\"\n\t\"net\"\n\t\"net/http\"\n\n\t\"go.uber.org/fx\"\n\t\"go.uber.org/fx/fxevent\"\n\t\"go.uber.org/zap\"\n)\n\nfunc main() {\n\tfx.New(\n\t\tfx.WithLogger(func(log *zap.Logger) fxevent.Logger {\n\t\t\treturn &fxevent.ZapLogger{Logger: log}\n\t\t}),\n\t\t// --8<-- [start:provides]\n\t\tfx.Provide(\n\t\t\tNewHTTPServer,\n\t\t\tNewServeMux,\n\t\t\tfx.Annotate(\n\t\t\t\tNewEchoHandler,\n\t\t\t\tfx.As(new(Route)),\n\t\t\t),\n\t\t\tzap.NewExample,\n\t\t),\n\t\t// --8<-- [end:provides]\n\t\tfx.Invoke(func(*http.Server) {}),\n\t).Run()\n}\n\n// --8<-- [start:route]\n\n// Route is an http.Handler that knows the mux pattern\n// under which it will be registered.\ntype Route interface {\n\thttp.Handler\n\n\t// Pattern reports the path at which this is registered.\n\tPattern() string\n}\n\n// --8<-- [end:route]\n\n// --8<-- [start:mux]\n\n// NewServeMux builds a ServeMux that will route requests\n// to the given Route.\nfunc NewServeMux(route Route) *http.ServeMux {\n\tmux := http.NewServeMux()\n\tmux.Handle(route.Pattern(), route)\n\treturn mux\n}\n\n// --8<-- [end:mux]\n\n// EchoHandler is an http.Handler that copies its request body\n// back to the response.\ntype EchoHandler struct {\n\tlog *zap.Logger\n}\n\n// NewEchoHandler builds a new EchoHandler.\nfunc NewEchoHandler(log *zap.Logger) *EchoHandler {\n\treturn &EchoHandler{log: log}\n}\n\n// Pattern reports the pattern under which\n// this handler should be registered.\n// --8<-- [start:echo-pattern]\nfunc (*EchoHandler) Pattern() string {\n\treturn \"/echo\"\n}\n\n// --8<-- [end:echo-pattern]\n\n// ServeHTTP handles an HTTP request to the /echo endpoint.\nfunc (h *EchoHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {\n\tif _, err := io.Copy(w, r.Body); err != nil {\n\t\th.log.Warn(\"Failed to handle request\", zap.Error(err))\n\t}\n}\n\n// NewHTTPServer builds an HTTP server that will begin serving requests\n// when the Fx application starts.\nfunc NewHTTPServer(lc fx.Lifecycle, mux *http.ServeMux, log *zap.Logger) *http.Server {\n\tsrv := &http.Server{Addr: \":8080\", Handler: mux}\n\tlc.Append(fx.Hook{\n\t\tOnStart: func(ctx context.Context) error {\n\t\t\tln, err := net.Listen(\"tcp\", srv.Addr)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tlog.Info(\"Starting HTTP server\", zap.String(\"addr\", srv.Addr))\n\t\t\tgo srv.Serve(ln)\n\t\t\treturn nil\n\t\t},\n\t\tOnStop: func(ctx context.Context) error {\n\t\t\treturn srv.Shutdown(ctx)\n\t\t},\n\t})\n\treturn srv\n}\n"
  },
  {
    "path": "docs/ex/get-started/05-registration/main_test.go",
    "content": "// Copyright (c) 2022 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage main\n\nimport (\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"go.uber.org/fx/docs/internal/apptest\"\n\t\"go.uber.org/fx/docs/internal/httptest\"\n)\n\nfunc TestRun(t *testing.T) {\n\tapptest.Start(t, main)\n\n\tgot := httptest.PostSuccess(t, \"http://127.0.0.1:8080/echo\", \"great success\")\n\tassert.Equal(t, \"great success\", got)\n}\n"
  },
  {
    "path": "docs/ex/get-started/06-another-handler/main.go",
    "content": "// Copyright (c) 2022 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"io\"\n\t\"net\"\n\t\"net/http\"\n\n\t\"go.uber.org/fx\"\n\t\"go.uber.org/fx/fxevent\"\n\t\"go.uber.org/zap\"\n)\n\nfunc main() {\n\tfx.New(\n\t\tfx.WithLogger(func(log *zap.Logger) fxevent.Logger {\n\t\t\treturn &fxevent.ZapLogger{Logger: log}\n\t\t}),\n\t\t// --8<-- [start:mux-provide]\n\t\tfx.Provide(\n\t\t\tNewHTTPServer,\n\t\t\tfx.Annotate(\n\t\t\t\tNewServeMux,\n\t\t\t\tfx.ParamTags(`name:\"echo\"`, `name:\"hello\"`),\n\t\t\t),\n\t\t\t// --8<-- [end:mux-provide]\n\t\t\t// --8<-- [start:hello-provide-partial-1]\n\t\t\t// --8<-- [start:route-provides]\n\t\t\tfx.Annotate(\n\t\t\t\tNewEchoHandler,\n\t\t\t\tfx.As(new(Route)),\n\t\t\t\t// --8<-- [end:hello-provide-partial-1]\n\t\t\t\tfx.ResultTags(`name:\"echo\"`),\n\t\t\t// --8<-- [start:hello-provide-partial-2]\n\t\t\t),\n\t\t\tfx.Annotate(\n\t\t\t\tNewHelloHandler,\n\t\t\t\tfx.As(new(Route)),\n\t\t\t\t// --8<-- [end:hello-provide-partial-2]\n\t\t\t\tfx.ResultTags(`name:\"hello\"`),\n\t\t\t\t// --8<-- [start:hello-provide-partial-3]\n\t\t\t),\n\t\t\t// --8<-- [end:hello-provide-partial-3]\n\t\t\t// --8<-- [end:route-provides]\n\t\t\tzap.NewExample,\n\t\t),\n\t\tfx.Invoke(func(*http.Server) {}),\n\t).Run()\n}\n\n// Route is an http.Handler that knows the mux pattern\n// under which it will be registered.\ntype Route interface {\n\thttp.Handler\n\n\t// Pattern reports the path at which this is registered.\n\tPattern() string\n}\n\n// --8<-- [start:mux]\n\n// NewServeMux builds a ServeMux that will route requests\n// to the given routes.\nfunc NewServeMux(route1, route2 Route) *http.ServeMux {\n\tmux := http.NewServeMux()\n\tmux.Handle(route1.Pattern(), route1)\n\tmux.Handle(route2.Pattern(), route2)\n\treturn mux\n}\n\n// --8<-- [end:mux]\n\n// --8<-- [start:hello-init]\n\n// HelloHandler is an HTTP handler that\n// prints a greeting to the user.\ntype HelloHandler struct {\n\tlog *zap.Logger\n}\n\n// NewHelloHandler builds a new HelloHandler.\nfunc NewHelloHandler(log *zap.Logger) *HelloHandler {\n\treturn &HelloHandler{log: log}\n}\n\n// --8<-- [end:hello-init]\n\n// Pattern reports the pattern under which\n// this handler should be registered.\n// --8<-- [start:hello-methods-1]\nfunc (*HelloHandler) Pattern() string {\n\treturn \"/hello\"\n}\n\n// --8<-- [end:hello-methods-1]\n\n// ServeHTTP handles an HTTP request to the /hello endpoint.\n// --8<-- [start:hello-methods-2]\nfunc (h *HelloHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {\n\tbody, err := io.ReadAll(r.Body)\n\tif err != nil {\n\t\th.log.Error(\"Failed to read request\", zap.Error(err))\n\t\thttp.Error(w, \"Internal server error\", http.StatusInternalServerError)\n\t\treturn\n\t}\n\n\tif _, err := fmt.Fprintf(w, \"Hello, %s\\n\", body); err != nil {\n\t\th.log.Error(\"Failed to write response\", zap.Error(err))\n\t\thttp.Error(w, \"Internal server error\", http.StatusInternalServerError)\n\t\treturn\n\t}\n}\n\n// --8<-- [end:hello-methods-2]\n\n// EchoHandler is an http.Handler that copies its request body\n// back to the response.\ntype EchoHandler struct {\n\tlog *zap.Logger\n}\n\n// NewEchoHandler builds a new EchoHandler.\nfunc NewEchoHandler(log *zap.Logger) *EchoHandler {\n\treturn &EchoHandler{log: log}\n}\n\n// Pattern reports the pattern under which\n// this handler should be registered.\nfunc (*EchoHandler) Pattern() string {\n\treturn \"/echo\"\n}\n\n// ServeHTTP handles an HTTP request to the /echo endpoint.\nfunc (h *EchoHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {\n\tif _, err := io.Copy(w, r.Body); err != nil {\n\t\th.log.Warn(\"Failed to handle request\", zap.Error(err))\n\t}\n}\n\n// NewHTTPServer builds an HTTP server that will begin serving requests\n// when the Fx application starts.\nfunc NewHTTPServer(lc fx.Lifecycle, mux *http.ServeMux, log *zap.Logger) *http.Server {\n\tsrv := &http.Server{Addr: \":8080\", Handler: mux}\n\tlc.Append(fx.Hook{\n\t\tOnStart: func(ctx context.Context) error {\n\t\t\tln, err := net.Listen(\"tcp\", srv.Addr)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tlog.Info(\"Starting HTTP server\", zap.String(\"addr\", srv.Addr))\n\t\t\tgo srv.Serve(ln)\n\t\t\treturn nil\n\t\t},\n\t\tOnStop: func(ctx context.Context) error {\n\t\t\treturn srv.Shutdown(ctx)\n\t\t},\n\t})\n\treturn srv\n}\n"
  },
  {
    "path": "docs/ex/get-started/06-another-handler/main_test.go",
    "content": "// Copyright (c) 2022 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage main\n\nimport (\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"go.uber.org/fx/docs/internal/apptest\"\n\t\"go.uber.org/fx/docs/internal/httptest\"\n)\n\nfunc TestRun(t *testing.T) {\n\tapptest.Start(t, main)\n\n\tt.Run(\"echo\", func(t *testing.T) {\n\t\tgot := httptest.PostSuccess(t, \"http://127.0.0.1:8080/echo\", \"great success\")\n\t\tassert.Equal(t, \"great success\", got)\n\t})\n\n\tt.Run(\"hello\", func(t *testing.T) {\n\t\tgot := httptest.PostSuccess(t, \"http://127.0.0.1:8080/hello\", \"world\")\n\t\tassert.Equal(t, \"Hello, world\\n\", got)\n\t})\n}\n"
  },
  {
    "path": "docs/ex/get-started/07-many-handlers/main.go",
    "content": "// Copyright (c) 2022 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"io\"\n\t\"net\"\n\t\"net/http\"\n\n\t\"go.uber.org/fx\"\n\t\"go.uber.org/fx/fxevent\"\n\t\"go.uber.org/zap\"\n)\n\nfunc main() {\n\tfx.New(\n\t\tfx.WithLogger(func(log *zap.Logger) fxevent.Logger {\n\t\t\treturn &fxevent.ZapLogger{Logger: log}\n\t\t}),\n\t\t// --8<-- [start:mux-provide]\n\t\t// --8<-- [start:route-provides-1]\n\t\tfx.Provide(\n\t\t\t// --8<-- [end:route-provides-1]\n\t\t\tNewHTTPServer,\n\t\t\tfx.Annotate(\n\t\t\t\tNewServeMux,\n\t\t\t\tfx.ParamTags(`group:\"routes\"`),\n\t\t\t),\n\t\t\t// --8<-- [end:mux-provide]\n\t\t\t// --8<-- [start:route-provides-2]\n\t\t\tAsRoute(NewEchoHandler),\n\t\t\tAsRoute(NewHelloHandler),\n\t\t\tzap.NewExample,\n\t\t),\n\t\t// --8<-- [end:route-provides-2]\n\t\tfx.Invoke(func(*http.Server) {}),\n\t).Run()\n}\n\n// --8<-- [start:AsRoute]\n\n// AsRoute annotates the given constructor to state that\n// it provides a route to the \"routes\" group.\nfunc AsRoute(f any) any {\n\treturn fx.Annotate(\n\t\tf,\n\t\tfx.As(new(Route)),\n\t\tfx.ResultTags(`group:\"routes\"`),\n\t)\n}\n\n// --8<-- [end:AsRoute]\n\n// Route is an http.Handler that knows the mux pattern\n// under which it will be registered.\ntype Route interface {\n\thttp.Handler\n\n\t// Pattern reports the path at which this is registered.\n\tPattern() string\n}\n\n// NewServeMux builds a ServeMux that will route requests\n// to the given routes.\n// --8<-- [start:mux]\nfunc NewServeMux(routes []Route) *http.ServeMux {\n\tmux := http.NewServeMux()\n\tfor _, route := range routes {\n\t\tmux.Handle(route.Pattern(), route)\n\t}\n\treturn mux\n}\n\n// --8<-- [end:mux]\n\n// HelloHandler is an HTTP handler that\n// prints a greeting to the user.\ntype HelloHandler struct {\n\tlog *zap.Logger\n}\n\n// NewHelloHandler builds a HelloHandler.\nfunc NewHelloHandler(log *zap.Logger) *HelloHandler {\n\treturn &HelloHandler{log: log}\n}\n\n// Pattern reports the mux pattern under which\n// this handler should be registered.\nfunc (*HelloHandler) Pattern() string {\n\treturn \"/hello\"\n}\n\n// ServeHTTP handles a request to the /hello endpoint.\nfunc (h *HelloHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {\n\tbody, err := io.ReadAll(r.Body)\n\tif err != nil {\n\t\th.log.Error(\"Failed to read request\", zap.Error(err))\n\t\thttp.Error(w, \"Internal server error\", http.StatusInternalServerError)\n\t\treturn\n\t}\n\n\tif _, err := fmt.Fprintf(w, \"Hello, %s\\n\", body); err != nil {\n\t\th.log.Error(\"Failed to write response\", zap.Error(err))\n\t\thttp.Error(w, \"Internal server error\", http.StatusInternalServerError)\n\t\treturn\n\t}\n}\n\n// EchoHandler is an http.Handler that copies its request body\n// back to the response.\ntype EchoHandler struct {\n\tlog *zap.Logger\n}\n\n// NewEchoHandler builds a new EchoHandler.\nfunc NewEchoHandler(log *zap.Logger) *EchoHandler {\n\treturn &EchoHandler{log: log}\n}\n\n// Pattern reports the mux pattern under which\n// this handler should be registered.\nfunc (*EchoHandler) Pattern() string {\n\treturn \"/echo\"\n}\n\n// ServeHTTP handles an HTTP request to the /echo endpoint.\nfunc (h *EchoHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {\n\tif _, err := io.Copy(w, r.Body); err != nil {\n\t\th.log.Warn(\"Failed to handle request\", zap.Error(err))\n\t}\n}\n\n// NewHTTPServer builds an HTTP server that will begin serving requests\n// when the Fx application starts.\nfunc NewHTTPServer(lc fx.Lifecycle, mux *http.ServeMux, log *zap.Logger) *http.Server {\n\tsrv := &http.Server{Addr: \":8080\", Handler: mux}\n\tlc.Append(fx.Hook{\n\t\tOnStart: func(ctx context.Context) error {\n\t\t\tln, err := net.Listen(\"tcp\", srv.Addr)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tlog.Info(\"Starting HTTP server\", zap.String(\"addr\", srv.Addr))\n\t\t\tgo srv.Serve(ln)\n\t\t\treturn nil\n\t\t},\n\t\tOnStop: func(ctx context.Context) error {\n\t\t\treturn srv.Shutdown(ctx)\n\t\t},\n\t})\n\treturn srv\n}\n"
  },
  {
    "path": "docs/ex/get-started/07-many-handlers/main_test.go",
    "content": "// Copyright (c) 2022 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage main\n\nimport (\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"go.uber.org/fx/docs/internal/apptest\"\n\t\"go.uber.org/fx/docs/internal/httptest\"\n)\n\nfunc TestRun(t *testing.T) {\n\tapptest.Start(t, main)\n\n\tt.Run(\"echo\", func(t *testing.T) {\n\t\tgot := httptest.PostSuccess(t, \"http://127.0.0.1:8080/echo\", \"great success\")\n\t\tassert.Equal(t, \"great success\", got)\n\t})\n\n\tt.Run(\"hello\", func(t *testing.T) {\n\t\tgot := httptest.PostSuccess(t, \"http://127.0.0.1:8080/hello\", \"world\")\n\t\tassert.Equal(t, \"Hello, world\\n\", got)\n\t})\n}\n"
  },
  {
    "path": "docs/ex/modules/module.go",
    "content": "// Copyright (c) 2022 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage modules\n\nimport (\n\t\"go.uber.org/fx\"\n\t\"go.uber.org/zap\"\n)\n\n// Module is an example of an Fx module's skeleton.\n// --8<-- [start:start]\nvar Module = fx.Module(\"server\",\n\t// --8<-- [end:start]\n\t// --8<-- [start:provide]\n\tfx.Provide(\n\t\tNew,\n\t),\n\t// --8<-- [end:provide]\n\t// --8<-- [start:privateProvide]\n\tfx.Provide(\n\t\tfx.Private,\n\t\tparseConfig,\n\t),\n\t// --8<-- [end:privateProvide]\n\t// --8<-- [start:invoke]\n\tfx.Invoke(startServer),\n\t// --8<-- [end:invoke]\n\t// --8<-- [start:decorate]\n\tfx.Decorate(wrapLogger),\n\t// --8<-- [end:decorate]\n// --8<-- [start:endProvide]\n)\n\n// --8<-- [end:endProvide]\n\n// Config is the configuration of the server.\n// --8<-- [start:config]\ntype Config struct {\n\tAddr string `yaml:\"addr\"`\n}\n\n// --8<-- [end:config]\n\nfunc parseConfig() (Config, error) {\n\treturn Config{}, nil\n}\n\n// Params defines the parameters of the module.\n// --8<-- [start:params]\ntype Params struct {\n\tfx.In\n\n\tLog    *zap.Logger\n\tConfig Config\n}\n\n// --8<-- [end:params]\n\n// Result defines the results of the module.\n// --8<-- [start:result]\ntype Result struct {\n\tfx.Out\n\n\tServer *Server\n}\n\n// --8<-- [end:result]\n\n// New builds a new server.\n// --8<-- [start:new]\nfunc New(p Params) (Result, error) {\n\t// --8<-- [end:new]\n\treturn Result{\n\t\tServer: &Server{},\n\t}, nil\n}\n\n// Server is the server.\ntype Server struct{}\n\n// Start starts the server.\nfunc (*Server) Start() error {\n\treturn nil\n}\n\nfunc startServer(srv *Server) error {\n\treturn srv.Start()\n}\n\nfunc wrapLogger(log *zap.Logger) *zap.Logger {\n\treturn log.With(zap.String(\"component\", \"mymodule\"))\n}\n"
  },
  {
    "path": "docs/ex/modules/module_test.go",
    "content": "// Copyright (c) 2022 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage modules\n\nimport (\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"go.uber.org/fx\"\n\t\"go.uber.org/fx/fxtest\"\n\t\"go.uber.org/zap\"\n)\n\nfunc TestModule(t *testing.T) {\n\tvar got *Server\n\tapp := fxtest.New(t,\n\t\tModule,\n\t\tfx.Supply(zap.NewNop()),\n\t\tfx.Populate(&got),\n\t)\n\tapp.RequireStart().RequireStop()\n\tassert.NotNil(t, got)\n}\n"
  },
  {
    "path": "docs/ex/parameter-objects/define.go",
    "content": "// Copyright (c) 2022 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage paramobject\n\nimport (\n\t\"net/http\"\n\n\t\"go.uber.org/fx\"\n\t\"go.uber.org/zap\"\n)\n\n// Client sends requests to a server.\ntype Client struct {\n\turl  string\n\thttp *http.Client\n\tlog  *zap.Logger\n}\n\n// ClientConfig defines the configuration for the client.\ntype ClientConfig struct {\n\tURL string\n}\n\n// ClientParams defines the parameters necessary to build a client.\n// --8<-- [start:empty-1]\n// --8<-- [start:fxin]\n// --8<-- [start:fields]\ntype ClientParams struct {\n\t// --8<-- [end:empty-1]\n\tfx.In\n\t// --8<-- [end:fxin]\n\n\tConfig     ClientConfig\n\tHTTPClient *http.Client\n\t// --8<-- [start:empty-2]\n}\n\n// --8<-- [end:fields]\n// --8<-- [end:empty-2]\n\n// NewClient builds a new client.\n// --8<-- [start:takeparam]\n// --8<-- [start:consume]\nfunc NewClient(p ClientParams) (*Client, error) {\n\t// --8<-- [end:takeparam]\n\treturn &Client{\n\t\turl:  p.Config.URL,\n\t\thttp: p.HTTPClient,\n\t\t// ...\n\t}, nil\n\t// --8<-- [end:consume]\n}\n"
  },
  {
    "path": "docs/ex/parameter-objects/define_test.go",
    "content": "// Copyright (c) 2022 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage paramobject\n\nimport (\n\t\"net/http\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"go.uber.org/fx\"\n\t\"go.uber.org/fx/fxtest\"\n)\n\nfunc TestClientParams(t *testing.T) {\n\tclient := new(http.Client)\n\tvar got *Client\n\tapp := fxtest.New(t,\n\t\tfx.Supply(\n\t\t\tClientConfig{URL: \"http://example.com\"},\n\t\t\tclient,\n\t\t),\n\t\tfx.Provide(NewClient),\n\t\tfx.Populate(&got),\n\t)\n\tapp.RequireStart().RequireStop()\n\n\tassert.Equal(t, \"http://example.com\", got.url)\n\n\t// == instead of assert.Equal to match pointers.\n\tassert.True(t, client == got.http, \"HTTP client did not match\")\n}\n"
  },
  {
    "path": "docs/ex/parameter-objects/extend.go",
    "content": "// Copyright (c) 2022 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage paramobject\n\nimport (\n\t\"net/http\"\n\n\t\"go.uber.org/fx\"\n\t\"go.uber.org/zap\"\n)\n\n// Params defines the parameters of new.\n// --8<-- [start:start-1]\n// --8<-- [start:full]\ntype Params struct {\n\tfx.In\n\n\tConfig     ClientConfig\n\tHTTPClient *http.Client\n\t// --8<-- [end:start-1]\n\tLogger *zap.Logger `optional:\"true\"`\n\t// --8<-- [start:start-2]\n}\n\n// --8<-- [end:start-2]\n// --8<-- [end:full]\n\n// New builds a new Client.\n// --8<-- [start:start-3]\n// --8<-- [start:consume]\nfunc New(p Params) (*Client, error) {\n\t// --8<-- [end:start-3]\n\tlog := p.Logger\n\tif log == nil {\n\t\tlog = zap.NewNop()\n\t}\n\t// ...\n\t// --8<-- [end:consume]\n\n\treturn &Client{log: log}, nil\n}\n"
  },
  {
    "path": "docs/ex/parameter-objects/extend_test.go",
    "content": "// Copyright (c) 2022 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage paramobject\n\nimport (\n\t\"net/http\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"go.uber.org/fx\"\n\t\"go.uber.org/fx/fxtest\"\n\t\"go.uber.org/zap\"\n)\n\nfunc TestExtendParams(t *testing.T) {\n\tt.Run(\"absent\", func(t *testing.T) {\n\t\tvar got *Client\n\t\tapp := fxtest.New(t,\n\t\t\tfx.Supply(\n\t\t\t\tClientConfig{},\n\t\t\t\tnew(http.Client),\n\t\t\t),\n\t\t\tfx.Provide(New),\n\t\t\tfx.Populate(&got),\n\t\t)\n\t\tapp.RequireStart().RequireStop()\n\n\t\tassert.NotNil(t, got.log)\n\t})\n\n\tt.Run(\"present\", func(t *testing.T) {\n\t\tvar got *Client\n\t\tlog := zap.NewExample()\n\t\tapp := fxtest.New(t,\n\t\t\tfx.Supply(\n\t\t\t\tClientConfig{},\n\t\t\t\tnew(http.Client),\n\t\t\t\tlog,\n\t\t\t),\n\t\t\tfx.Provide(New),\n\t\t\tfx.Populate(&got),\n\t\t)\n\t\tapp.RequireStart().RequireStop()\n\n\t\t// Log must be what we provided.\n\t\tassert.True(t, got.log == log, \"log did not match\")\n\t})\n}\n"
  },
  {
    "path": "docs/ex/result-objects/define.go",
    "content": "// Copyright (c) 2022 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage resultobject\n\nimport \"go.uber.org/fx\"\n\n// Client is a client to make requests.\ntype Client struct{}\n\n// ClientResult holds the result of NewClient.\n// --8<-- [start:empty-1]\n// --8<-- [start:fxout]\n// --8<-- [start:fields]\ntype ClientResult struct {\n\t// --8<-- [end:empty-1]\n\tfx.Out\n\t// --8<-- [end:fxout]\n\n\tClient *Client\n\t// --8<-- [start:empty-2]\n}\n\n// --8<-- [end:empty-2]\n// --8<-- [end:fields]\n\n// NewClient builds a new Client.\n// --8<-- [start:returnresult]\n// --8<-- [start:produce]\nfunc NewClient() (ClientResult, error) {\n\t// --8<-- [end:returnresult]\n\tclient := &Client{\n\t\t// ...\n\t}\n\treturn ClientResult{Client: client}, nil\n}\n\n// --8<-- [end:produce]\n"
  },
  {
    "path": "docs/ex/result-objects/define_test.go",
    "content": "// Copyright (c) 2022 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage resultobject\n\nimport (\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"go.uber.org/fx\"\n\t\"go.uber.org/fx/fxtest\"\n)\n\nfunc TestClientResult(t *testing.T) {\n\tvar got *Client\n\tapp := fxtest.New(t,\n\t\tfx.Provide(NewClient),\n\t\tfx.Populate(&got),\n\t)\n\tapp.RequireStart().RequireStop()\n\n\tassert.NotNil(t, got)\n}\n"
  },
  {
    "path": "docs/ex/result-objects/extend.go",
    "content": "// Copyright (c) 2022 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage resultobject\n\nimport \"go.uber.org/fx\"\n\n// Inspector inspects client state.\ntype Inspector struct{}\n\n// Result is the result of this module.\n// --8<-- [start:full]\n// --8<-- [start:start-1]\ntype Result struct {\n\tfx.Out\n\n\tClient *Client\n\t// --8<-- [end:start-1]\n\tInspector *Inspector\n\t// --8<-- [start:start-2]\n}\n\n// --8<-- [end:start-2]\n// --8<-- [end:full]\n\n// New builds a result.\n// --8<-- [start:start-3]\nfunc New() (Result, error) {\n\tclient := &Client{\n\t\t// ...\n\t}\n\t// --8<-- [start:produce]\n\treturn Result{\n\t\tClient: client,\n\t\t// --8<-- [end:start-3]\n\t\tInspector: &Inspector{\n\t\t\t// ...\n\t\t},\n\t\t// --8<-- [start:start-4]\n\t}, nil\n\t// --8<-- [end:start-4]\n\t// --8<-- [end:produce]\n}\n"
  },
  {
    "path": "docs/ex/result-objects/extend_test.go",
    "content": "// Copyright (c) 2022 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage resultobject\n\nimport (\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"go.uber.org/fx\"\n\t\"go.uber.org/fx/fxtest\"\n)\n\nfunc TestExtendResult(t *testing.T) {\n\tvar (\n\t\tclient    *Client\n\t\tinspector *Inspector\n\t)\n\tapp := fxtest.New(t,\n\t\tfx.Provide(New),\n\t\tfx.Populate(&client, &inspector),\n\t)\n\tapp.RequireStart().RequireStop()\n\n\tassert.NotNil(t, client)\n\tassert.NotNil(t, inspector)\n}\n"
  },
  {
    "path": "docs/ex/value-groups/consume/annotate.go",
    "content": "// Copyright (c) 2022 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage consume\n\nimport \"go.uber.org/fx\"\n\n// PlainModule is an unannotated NewEmitter.\nvar PlainModule = fx.Options(\n\t// --8<-- [start:provide-init]\n\tfx.Provide(\n\t\tNewEmitter,\n\t),\n\t// --8<-- [end:provide-init]\n)\n\n// AnnotateModule is the module defined in this file.\nvar AnnotateModule = fx.Options(\n\t// --8<-- [start:provide-wrap-1]\n\tfx.Provide(\n\t\t// --8<-- [start:provide-annotate]\n\t\tfx.Annotate(\n\t\t\tNewEmitter,\n\t\t\t// --8<-- [end:provide-wrap-1]\n\t\t\tfx.ParamTags(`group:\"watchers\"`),\n\t\t\t// --8<-- [start:provide-wrap-2]\n\t\t),\n\t\t// --8<-- [end:provide-annotate]\n\t),\n\t// --8<-- [end:provide-wrap-2]\n)\n\n// Emitter emits events\ntype Emitter struct{ ws []Watcher }\n\n// NewEmitter builds an emitter.\n// --8<-- [start:new-init]\n// --8<-- [start:new-consume]\nfunc NewEmitter(watchers []Watcher) (*Emitter, error) {\n\t// --8<-- [end:new-init]\n\tfor _, w := range watchers {\n\t\t// ...\n\t\t// --8<-- [end:new-consume]\n\t\t_ = w // unused\n\t}\n\treturn &Emitter{ws: watchers}, nil\n}\n\n// EmitterFromModule is a module that holds EmitterFrom.\nvar EmitterFromModule = fx.Options(\n\tfx.Provide(\n\t\t// --8<-- [start:annotate-variadic]\n\t\tfx.Annotate(\n\t\t\tEmitterFrom,\n\t\t\tfx.ParamTags(`group:\"watchers\"`),\n\t\t),\n\t\t// --8<-- [end:annotate-variadic]\n\t),\n)\n\n// EmitterFrom builds an Emitter from the list of watchers.\n// --8<-- [start:new-variadic]\nfunc EmitterFrom(watchers ...Watcher) (*Emitter, error) {\n\t// --8<-- [end:new-variadic]\n\treturn &Emitter{ws: watchers}, nil\n}\n"
  },
  {
    "path": "docs/ex/value-groups/consume/consume_test.go",
    "content": "// Copyright (c) 2022 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage consume\n\nimport (\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"go.uber.org/fx\"\n\t\"go.uber.org/fx/fxtest\"\n)\n\nfunc TestConsume(t *testing.T) {\n\ttests := []struct {\n\t\tname   string\n\t\tmodule fx.Option\n\t}{\n\t\t{\"Params\", ParamsModule},\n\t\t{\"Annotated\", AnnotateModule},\n\t\t{\"EmitterFrom\", EmitterFromModule},\n\t}\n\n\taddWatcher := fx.Annotate(\n\t\tfunc() struct{} {\n\t\t\treturn struct{}{}\n\t\t},\n\t\tfx.As(new(Watcher)),\n\t\tfx.ResultTags(`group:\"watchers\"`),\n\t)\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tvar e *Emitter\n\t\t\tapp := fxtest.New(t,\n\t\t\t\ttt.module,\n\t\t\t\tfx.Provide(\n\t\t\t\t\taddWatcher,\n\t\t\t\t\taddWatcher,\n\t\t\t\t\taddWatcher,\n\t\t\t\t),\n\t\t\t\tfx.Populate(&e),\n\t\t\t)\n\t\t\tapp.RequireStart().RequireStop()\n\n\t\t\tassert.Len(t, e.ws, 3)\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "docs/ex/value-groups/consume/param.go",
    "content": "// Copyright (c) 2022 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage consume\n\nimport \"go.uber.org/fx\"\n\n// Watcher watches for events.\ntype Watcher interface{}\n\n// ParamsModule is the module defined in this file.\nvar ParamsModule = fx.Options(\n\t// --8<-- [start:provide]\n\tfx.Provide(New),\n\t// --8<-- [end:provide]\n)\n\n// Params is a parameter object.\n// --8<-- [start:param-tagged]\n// --8<-- [start:param-init-1]\ntype Params struct {\n\tfx.In\n\n\t// ...\n\t// --8<-- [end:param-init-1]\n\tWatchers []Watcher `group:\"watchers\"`\n\t// --8<-- [start:param-init-2]\n}\n\n// --8<-- [end:param-init-2]\n// --8<-- [end:param-tagged]\n\n// Result is a list of watchers.\ntype Result struct {\n\tfx.Out\n\n\tEmitter *Emitter\n}\n\n// New consumes a value group.\n// --8<-- [start:new-init]\n// --8<-- [start:new-consume]\nfunc New(p Params) (Result, error) {\n\t// ...\n\t// --8<-- [end:new-init]\n\tfor _, w := range p.Watchers {\n\t\t// ...\n\t\t// --8<-- [end:new-consume]\n\t\t_ = w // unused\n\t}\n\treturn Result{\n\t\tEmitter: &Emitter{ws: p.Watchers},\n\t}, nil\n}\n"
  },
  {
    "path": "docs/ex/value-groups/feed/annotate.go",
    "content": "// Copyright (c) 2022 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage feed\n\nimport \"go.uber.org/fx\"\n\n// AnnotateModule is the module defined in this file.\nvar AnnotateModule = fx.Options(\n\t// --8<-- [start:provide-init]\n\tfx.Provide(\n\t\tNewWatcher,\n\t),\n\t// --8<-- [end:provide-init]\n\t// --8<-- [start:provide-wrap-1]\n\tfx.Provide(\n\t\t// --8<-- [start:provide-annotate]\n\t\tfx.Annotate(\n\t\t\tNewWatcher,\n\t\t\t// --8<-- [end:provide-wrap-1]\n\t\t\tfx.ResultTags(`group:\"watchers\"`),\n\t\t\t// --8<-- [start:provide-wrap-2]\n\t\t),\n\t\t// --8<-- [end:provide-annotate]\n\t),\n\t// --8<-- [end:provide-wrap-2]\n)\n\n// FileWatcher watches files.\ntype FileWatcher struct{}\n\n// FileWatcherModule provides a FileWatcher as a Watcher.\nvar FileWatcherModule = fx.Options(\n\tfx.Provide(\n\t\t// --8<-- [start:annotate-fw]\n\t\tfx.Annotate(\n\t\t\tNewFileWatcher,\n\t\t\tfx.As(new(Watcher)),\n\t\t\tfx.ResultTags(`group:\"watchers\"`),\n\t\t),\n\t\t// --8<-- [end:annotate-fw]\n\t),\n)\n\n// NewFileWatcher builds a new file watcher.\n// --8<-- [start:new-fw-init]\nfunc NewFileWatcher( /* ... */ ) (*FileWatcher, error) {\n\t// --8<-- [end:new-fw-init]\n\treturn &FileWatcher{\n\t\t// ...\n\t}, nil\n}\n\n// NewWatcher builds a watcher.\n// --8<-- [start:new-init]\nfunc NewWatcher( /* ... */ ) (Watcher, error) {\n\t// ...\n\t// --8<-- [end:new-init]\n\n\treturn &FileWatcher{\n\t\t// ...\n\t}, nil\n}\n"
  },
  {
    "path": "docs/ex/value-groups/feed/feed_test.go",
    "content": "// Copyright (c) 2022 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage feed\n\nimport (\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"go.uber.org/fx\"\n\t\"go.uber.org/fx/fxtest\"\n)\n\nfunc TestWatcherModules(t *testing.T) {\n\ttests := []struct {\n\t\tname   string\n\t\tmodule fx.Option\n\t}{\n\t\t{\"Results\", ResultModule},\n\t\t{\"Annotated\", AnnotateModule},\n\t\t{\"FileWatcher\", FileWatcherModule},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tvar out struct {\n\t\t\t\tfx.In\n\n\t\t\t\tWatchers []Watcher `group:\"watchers\"`\n\t\t\t}\n\t\t\tapp := fxtest.New(t,\n\t\t\t\ttt.module,\n\t\t\t\tfx.Populate(&out),\n\t\t\t)\n\t\t\tapp.RequireStart().RequireStop()\n\n\t\t\tassert.Len(t, out.Watchers, 1)\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "docs/ex/value-groups/feed/result.go",
    "content": "// Copyright (c) 2022 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage feed\n\nimport \"go.uber.org/fx\"\n\n// ResultModule is the module defined in this file.\nvar ResultModule = fx.Options(\n\t// --8<-- [start:provide]\n\tfx.Provide(New),\n\t// --8<-- [end:provide]\n)\n\n// Watcher watches for events.\ntype Watcher interface{}\n\ntype watcher struct{}\n\n// Result is the result of an operation.\n// --8<-- [start:result-tagged]\n// --8<-- [start:result-init-1]\ntype Result struct {\n\tfx.Out\n\n\t// ...\n\t// --8<-- [end:result-init-1]\n\tWatcher Watcher `group:\"watchers\"`\n\t// --8<-- [start:result-init-2]\n}\n\n// --8<-- [end:result-init-2]\n// --8<-- [end:result-tagged]\n\n// New produces a result object.\n// --8<-- [start:new-init-1]\n// --8<-- [start:new-watcher]\nfunc New( /* ... */ ) (Result, error) {\n\t// ...\n\t// --8<-- [end:new-init-1]\n\twatcher := &watcher{\n\t\t// ...\n\t}\n\n\t// --8<-- [start:new-init-2]\n\treturn Result{\n\t\t// ...\n\t\tWatcher: watcher,\n\t}, nil\n}\n\n// --8<-- [end:new-watcher]\n// --8<-- [end:new-init-2]\n"
  },
  {
    "path": "docs/go.mod",
    "content": "module go.uber.org/fx/docs\n\ngo 1.24\n\nrequire (\n\tgithub.com/stretchr/testify v1.8.1\n\tgo.uber.org/fx v1.18.2\n\tgo.uber.org/zap v1.26.0\n)\n\nrequire (\n\tgithub.com/davecgh/go-spew v1.1.1 // indirect\n\tgithub.com/pmezard/go-difflib v1.0.0 // indirect\n\tgo.uber.org/dig v1.19.0 // indirect\n\tgo.uber.org/multierr v1.10.0 // indirect\n\tgolang.org/x/sys v0.0.0-20220412211240-33da011f77ad // indirect\n\tgopkg.in/yaml.v3 v3.0.1 // indirect\n)\n\nreplace go.uber.org/fx => ../\n"
  },
  {
    "path": "docs/go.sum",
    "content": "github.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/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/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 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=\ngithub.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=\ngo.uber.org/dig v1.19.0 h1:BACLhebsYdpQ7IROQ1AGPjrXcP5dF80U3gKoFzbaq/4=\ngo.uber.org/dig v1.19.0/go.mod h1:Us0rSJiThwCv2GteUN0Q7OKvU7n5J4dxZ9JKUXozFdE=\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.10.0 h1:S0h4aNzvfcFsC3dRF1jLoaov7oRaKqRGC/pUEJ2yvPQ=\ngo.uber.org/multierr v1.10.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/sys v0.0.0-20220412211240-33da011f77ad h1:ntjMns5wyP/fN65tdBD4g8J5w8n015+iIIs9rtjXkY0=\ngolang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=\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": "docs/internal/apptest/run.go",
    "content": "// Copyright (c) 2022 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage apptest\n\nimport (\n\t\"bufio\"\n\t\"context\"\n\t\"encoding/json\"\n\t\"os\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"go.uber.org/fx/docs/internal/exectest\"\n\t\"go.uber.org/fx/docs/internal/test\"\n)\n\n// StartOption is an option for the Start function.\ntype StartOption interface{ apply(*startOptions) }\n\ntype startOptions struct {\n\tIsRunning func(string) bool\n\tTimeout   time.Duration\n}\n\ntype isRunningOption func(string) bool\n\n// IsRunning customizes how Start determines\n// whether a log statement represents a running application.\n//\n// Defaults to DefaultIsRunning.\nfunc IsRunning(f func(string) bool) StartOption {\n\treturn isRunningOption(f)\n}\n\nfunc (o isRunningOption) apply(opts *startOptions) {\n\topts.IsRunning = o\n}\n\n// DefaultIsRunning looks for lines in Fx's log output,\n// which match either the ConsoleLogger or the ZapLogger's\n// output,\n// and represent that the application is running\n// and ready to receive requests.\nfunc DefaultIsRunning(line string) bool {\n\t// ConsoleLogger\n\tif strings.Contains(line, \"[Fx] RUNNING\") {\n\t\treturn true\n\t}\n\n\t// ZapLogger\n\tvar log struct {\n\t\tMsg string `json:\"msg\"`\n\t}\n\tif err := json.Unmarshal([]byte(line), &log); err == nil {\n\t\treturn log.Msg == \"started\"\n\t}\n\n\treturn false\n}\n\ntype timeoutOption time.Duration\n\n// Timeout specifies the duration after which we'll\n// stop waiting for the application to start up.\n//\n// Defaults to 5s.\nfunc Timeout(t time.Duration) StartOption {\n\treturn timeoutOption(t)\n}\n\nfunc (o timeoutOption) apply(opts *startOptions) {\n\topts.Timeout = time.Duration(o)\n}\n\n// Start starts the Fx application that main represents,\n// and blocks until isRunning(line) reports true for a line of output.\n// When the test exits, this signals for the application to stop,\n// and waits for it to stop.\nfunc Start(t test.T, main func(), options ...StartOption) {\n\tt.Helper()\n\n\topts := startOptions{\n\t\tTimeout:   5 * time.Second,\n\t\tIsRunning: DefaultIsRunning,\n\t}\n\tfor _, o := range options {\n\t\to.apply(&opts)\n\t}\n\n\tcmd := exectest.Command(t, main)\n\n\tr := exectest.StartWithOutput(t, cmd)\n\tdone := make(chan struct{})\n\tunblock := make(chan struct{})\n\tgo func() {\n\t\tdefer close(done)\n\n\t\tvar found bool\n\t\tscan := bufio.NewScanner(r)\n\t\tfor scan.Scan() {\n\t\t\tline := scan.Text()\n\t\t\tt.Logf(\"%s\", line)\n\t\t\tif !found && opts.IsRunning(line) {\n\t\t\t\tfound = true\n\t\t\t\tclose(unblock)\n\t\t\t}\n\t\t}\n\t\tassert.NoError(t, scan.Err(), \"scan error\")\n\t}()\n\tt.Cleanup(func() {\n\t\tassert.NoError(t, cmd.Process.Signal(os.Interrupt), \"send SIGINT\")\n\t\t<-done\n\t})\n\n\tctx, cancel := context.WithTimeout(context.Background(), opts.Timeout)\n\tdefer cancel()\n\n\tselect {\n\tcase <-unblock:\n\t\treturn\n\n\tcase <-done:\n\t\t// If the application exited without printing the Running\n\t\t// message, something went wrong.\n\t\t// Fail the test.\n\t\tt.Errorf(\"application exited unexpectedly\")\n\t\tt.FailNow()\n\n\tcase <-ctx.Done():\n\t\t// Application did not start within the specified timeout.\n\t\tt.Errorf(\"application did not start in %v\", opts.Timeout)\n\t\tt.FailNow()\n\t}\n}\n"
  },
  {
    "path": "docs/internal/apptest/run_test.go",
    "content": "// Copyright (c) 2022 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage apptest\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"os/signal\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\t\"go.uber.org/fx/docs/internal/test\"\n)\n\nfunc waitForInterrupt() {\n\tch := make(chan os.Signal, 1)\n\tsignal.Notify(ch, os.Interrupt)\n\t<-ch\n}\n\nfunc TestStart_FxLogger(t *testing.T) {\n\tStart(t, func() {\n\t\tfmt.Println(\"Not Fx output\")\n\t\tfmt.Println(\"[Fx] RUNNING\")\n\t\twaitForInterrupt()\n\t\tfmt.Println(\"Interrupted\")\n\t})\n\t// If we get here, everything is okay.\n}\n\nfunc TestStart_ZapLogger(t *testing.T) {\n\tStart(t, func() {\n\t\tfmt.Println(\"Not Fx output\")\n\t\tfmt.Println(`{\"msg\": \"started\"}`)\n\t\twaitForInterrupt()\n\t\tfmt.Println(\"Interrupted\")\n\t})\n\t// If we get here, everything is okay.\n}\n\nfunc TestStart_Custom(t *testing.T) {\n\tStart(t, func() {\n\t\tfmt.Println(\"Not Fx output\")\n\t\tfmt.Println(\"Hello world\")\n\t\twaitForInterrupt()\n\t\tfmt.Println(\"Interrupted\")\n\t}, IsRunning(func(s string) bool {\n\t\treturn s == \"Hello world\"\n\t}))\n\t// If we get here, everything is okay.\n}\n\nfunc TestStart_UnexpectedExit(t *testing.T) {\n\tresult := test.WithFake(t, func(t test.T) {\n\t\tStart(t, func() {\n\t\t\tfmt.Println(\"Not what we want\")\n\t\t})\n\t})\n\n\tassert.True(t, result.Fatally, \"expected FailNow\")\n\trequire.Len(t, result.Errors, 1, \"expected an error message\")\n\tassert.Contains(t, result.Errors[0], \"application exited unexpectedly\")\n}\n\nfunc TestStart_Timoeut(t *testing.T) {\n\tresult := test.WithFake(t, func(t test.T) {\n\t\tStart(t, func() {\n\t\t\tfmt.Println(\"Not what we want\")\n\t\t\twaitForInterrupt()\n\t\t}, Timeout(time.Millisecond))\n\t})\n\n\tassert.True(t, result.Fatally, \"expected FailNow\")\n\trequire.Len(t, result.Errors, 1, \"expected an error message\")\n\tassert.Contains(t, result.Errors[0], \"application did not start\")\n}\n"
  },
  {
    "path": "docs/internal/exectest/cmd.go",
    "content": "// Copyright (c) 2022 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage exectest\n\nimport (\n\t\"os\"\n\t\"os/exec\"\n\t\"path/filepath\"\n\n\t\"github.com/stretchr/testify/require\"\n\t\"go.uber.org/fx/docs/internal/test\"\n)\n\n// Command builds an exec.Cmd that will run the given function as an external\n// executable.\n//\n// This operates by re-running the test executable to run only the current\n// test, and hijacking that test execution to run the main function.\n//\n//\tcmd := Command(t, func() { fmt.Println(\"hello\") })\n//\tgot, err := cmd.Output()\n//\t...\n//\tfmt.Println(string(got) == \"hello\\n\") // true\nfunc Command(t test.T, main func()) *exec.Cmd {\n\tt.Helper()\n\n\t// This messes up the hijacking sometimes.\n\t// Keep it simple -- only top level tests can do this.\n\trequire.NotContains(t, t.Name(), \"/\",\n\t\t\"exectest.Command cannot be used with subtests\")\n\n\tif filepath.Base(os.Args[0]) == t.Name() {\n\t\t// We can't get coverage for this block\n\t\t// because if the condition is true,\n\t\t// we're inside the subprocess.\n\t\tmain()\n\t\tos.Exit(0)\n\t}\n\n\texe, err := os.Executable()\n\trequire.NoError(t, err, \"determine executable\")\n\n\tcmd := exec.Command(exe, \"-test.run\", \"^\"+t.Name()+\"$\")\n\t// Args[0] is the value of os.Args[0] for the new executable.\n\t// os.Args[0] is allowed to be different from the command.\n\tcmd.Args[0] = t.Name()\n\treturn cmd\n}\n"
  },
  {
    "path": "docs/internal/exectest/cmd_test.go",
    "content": "// Copyright (c) 2022 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage exectest\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"os\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestCommandSuccess(t *testing.T) {\n\tcmd := Command(t, func() {\n\t\tfmt.Println(\"hello world\")\n\t})\n\n\tout, err := cmd.Output()\n\trequire.NoError(t, err)\n\tassert.Equal(t, \"hello world\\n\", string(out))\n\n\tassert.True(t, cmd.ProcessState.Exited(), \"must exit\")\n\tassert.Zero(t, cmd.ProcessState.ExitCode(), \"exit code\")\n}\n\nfunc TestCommandNonZero(t *testing.T) {\n\tcmd := Command(t, func() {\n\t\tfmt.Fprintln(os.Stderr, \"great sadness\")\n\t\tos.Exit(1)\n\t})\n\n\tvar stderr bytes.Buffer\n\tcmd.Stderr = &stderr\n\n\terr := cmd.Run()\n\trequire.Error(t, err, \"command must fail\")\n\n\tassert.Equal(t, \"great sadness\\n\", stderr.String())\n\tassert.True(t, cmd.ProcessState.Exited(), \"must exit\")\n\tassert.Equal(t, 1, cmd.ProcessState.ExitCode(), \"exit code\")\n}\n"
  },
  {
    "path": "docs/internal/exectest/output.go",
    "content": "// Copyright (c) 2022 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage exectest\n\nimport (\n\t\"io\"\n\t\"os\"\n\t\"os/exec\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\t\"go.uber.org/fx/docs/internal/test\"\n)\n\n// StartWithOutput starts the given command,\n// and returns an io.Reader that reads from both,\n// its stdout and stderr.\n//\n// At test end, the system will ensure that\n// the command finished running.\nfunc StartWithOutput(t test.T, cmd *exec.Cmd) io.Reader {\n\tt.Helper()\n\n\tr, w, err := os.Pipe()\n\trequire.NoError(t, err, \"create pipe\")\n\n\tcmd.Stdout = w\n\tcmd.Stderr = w\n\trequire.NoError(t, cmd.Start(), \"start command\")\n\t// Close the output writer because this process won't write to it\n\t// anymore. Only the spawned process will.\n\tassert.NoError(t, w.Close(), \"close output writer\")\n\tt.Cleanup(func() {\n\t\t_, err := cmd.Process.Wait()\n\t\tassert.NoError(t, err, \"wait for end\")\n\t\tassert.NoError(t, r.Close(), \"close output reader\")\n\t})\n\treturn r\n}\n"
  },
  {
    "path": "docs/internal/exectest/output_test.go",
    "content": "// Copyright (c) 2022 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage exectest\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"os\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestStartOutputReader_Stdout(t *testing.T) {\n\tcmd := Command(t, func() {\n\t\tfmt.Println(\"hello\")\n\t\tfmt.Println(\"world\")\n\t})\n\n\tr := StartWithOutput(t, cmd)\n\tgot, err := io.ReadAll(r)\n\trequire.NoError(t, err)\n\tassert.Equal(t, \"hello\\nworld\\n\", string(got))\n}\n\nfunc TestStartOutputReader_Stderr(t *testing.T) {\n\tcmd := Command(t, func() {\n\t\tfmt.Fprintln(os.Stderr, \"great\")\n\t\tfmt.Fprintln(os.Stderr, \"sadness\")\n\t})\n\n\tr := StartWithOutput(t, cmd)\n\tgot, err := io.ReadAll(r)\n\trequire.NoError(t, err)\n\tassert.Equal(t, \"great\\nsadness\\n\", string(got))\n}\n\nfunc TestStartOutputReader_Combined(t *testing.T) {\n\tcmd := Command(t, func() {\n\t\tfmt.Println(\"foo\")\n\t\tfmt.Fprintln(os.Stderr, \"bar\")\n\t\tfmt.Println(\"baz\")\n\t\tfmt.Fprintln(os.Stderr, \"qux\")\n\t})\n\n\tr := StartWithOutput(t, cmd)\n\tgot, err := io.ReadAll(r)\n\trequire.NoError(t, err)\n\tassert.Equal(t, \"foo\\nbar\\nbaz\\nqux\\n\", string(got))\n}\n"
  },
  {
    "path": "docs/internal/httptest/http.go",
    "content": "// Copyright (c) 2022 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage httptest\n\nimport (\n\t\"net/http\"\n\t\"strings\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\t\"go.uber.org/fx/docs/internal/iotest\"\n\t\"go.uber.org/fx/docs/internal/test\"\n)\n\n// PostSuccess makes an HTTP POST request to the given URL,\n// and returns the response body.\n//\n// PostSuccess uses a text/plain content type,\n// and expects a 200 status code.\nfunc PostSuccess(t test.T, url, body string) string {\n\tt.Helper()\n\n\tres, err := http.Post(url, \"text/plain\", strings.NewReader(body))\n\trequire.NoError(t, err, \"http post %q\", url)\n\tdefer func() {\n\t\tassert.NoError(t, res.Body.Close(), \"close response body\")\n\t}()\n\tassert.Equal(t, http.StatusOK, res.StatusCode, \"status code did not match\")\n\n\treturn iotest.ReadAll(t, res.Body)\n}\n"
  },
  {
    "path": "docs/internal/httptest/http_test.go",
    "content": "// Copyright (c) 2022 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage httptest\n\nimport (\n\t\"io\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"go.uber.org/fx/docs/internal/test\"\n)\n\nfunc TestPostSuccess(t *testing.T) {\n\tmux := http.NewServeMux()\n\tmux.HandleFunc(\"/success\", func(w http.ResponseWriter, r *http.Request) {\n\t\t_, err := io.Copy(w, r.Body)\n\t\tassert.NoError(t, err, \"copy request body\")\n\t})\n\tmux.HandleFunc(\"/failure\", func(w http.ResponseWriter, r *http.Request) {\n\t\thttp.NotFound(w, r)\n\t})\n\n\tsrv := httptest.NewServer(mux)\n\tdefer srv.Close()\n\n\tt.Run(\"success\", func(t *testing.T) {\n\t\tgot := PostSuccess(t, srv.URL+\"/success\", \"hello\")\n\t\tassert.Equal(t, \"hello\", got)\n\t})\n\n\tt.Run(\"failure\", func(t *testing.T) {\n\t\tresult := test.WithFake(t, func(t test.T) {\n\t\t\tPostSuccess(t, srv.URL+\"/failure\", \"hello\")\n\t\t})\n\t\tassert.True(t, result.Failed, \"test should fail\")\n\t\tassert.Len(t, result.Errors, 1)\n\t\tassert.Contains(t, result.Errors[0], \"status code did not match\")\n\t})\n}\n"
  },
  {
    "path": "docs/internal/iotest/read.go",
    "content": "// Copyright (c) 2022 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage iotest\n\nimport (\n\t\"io\"\n\n\t\"github.com/stretchr/testify/require\"\n\t\"go.uber.org/fx/docs/internal/test\"\n)\n\n// ReadAll is an alias for io.ReadAll that fails the current test\n// and halts execution if the read fails.\nfunc ReadAll(t test.T, r io.Reader) string {\n\tt.Helper()\n\n\tb, err := io.ReadAll(r)\n\trequire.NoError(t, err, \"read all output\")\n\treturn string(b)\n}\n"
  },
  {
    "path": "docs/internal/iotest/read_test.go",
    "content": "// Copyright (c) 2022 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage iotest\n\nimport (\n\t\"errors\"\n\t\"strings\"\n\t\"testing\"\n\t\"testing/iotest\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"go.uber.org/fx/docs/internal/test\"\n)\n\nfunc TestReadAll(t *testing.T) {\n\tt.Run(\"success\", func(t *testing.T) {\n\t\tgot := ReadAll(t, strings.NewReader(\"hello\"))\n\t\tassert.Equal(t, \"hello\", got)\n\t})\n\n\tt.Run(\"failure\", func(t *testing.T) {\n\t\tnewT := fakeT{T: t}\n\t\tReadAll(&newT, iotest.ErrReader(errors.New(\"great sadness\")))\n\t\tassert.True(t, newT.failed, \"test must have failed\")\n\t})\n}\n\ntype fakeT struct {\n\ttest.T\n\n\tfailed bool\n}\n\nfunc (t *fakeT) Errorf(msg string, args ...any) {\n\tt.failed = true\n}\n\nfunc (t *fakeT) FailNow() {\n\tt.failed = true\n}\n"
  },
  {
    "path": "docs/internal/test/fake.go",
    "content": "// Copyright (c) 2022 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage test\n\nimport (\n\t\"fmt\"\n\t\"runtime\"\n\t\"sync\"\n)\n\n// FakeReport is the result of an execution with a fake T.\ntype FakeReport struct {\n\tFailed  bool\n\tFatally bool\n\tErrors  []string\n}\n\n// WithFake runs the given function with a fake T.\n// Failures reported to the fake will not affect the current test.\n// When the given function finishes running,\n// WithFake produces a report of the test run.\nfunc WithFake(t T, f func(T)) FakeReport {\n\tfake := fakeT{T: t}\n\n\t// t.FailNow calls runtime.Goexit to kill the current goroutine.\n\t// To guard against that, we'll run the given function\n\t// in a separate goroutine and if the user calls t.FailNow,\n\t// we'll record it and continue execution here.\n\tdone := make(chan struct{})\n\tgo func() {\n\t\tdefer close(done)\n\t\tf(&fake)\n\t}()\n\t<-done\n\n\treturn FakeReport{\n\t\tFailed:  fake.failed,\n\t\tFatally: fake.fatal,\n\t\tErrors:  fake.errors,\n\t}\n}\n\n// fakeT is a fake implementation of a T.\ntype fakeT struct {\n\tT\n\tmu     sync.RWMutex\n\tfatal  bool\n\tfailed bool\n\terrors []string\n}\n\nvar _ T = (*fakeT)(nil)\n\nfunc (t *fakeT) Errorf(msg string, args ...any) {\n\tt.mu.Lock()\n\tdefer t.mu.Unlock()\n\n\tt.failed = true\n\tt.errors = append(t.errors, fmt.Sprintf(msg, args...))\n}\n\nfunc (t *fakeT) FailNow() {\n\tt.mu.Lock()\n\tdefer t.mu.Unlock()\n\n\tt.failed = true\n\tt.fatal = true\n\truntime.Goexit()\n}\n"
  },
  {
    "path": "docs/internal/test/fake_test.go",
    "content": "// Copyright (c) 2022 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage test\n\nimport (\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestWithFake_Success(t *testing.T) {\n\treport := WithFake(t, func(t T) {\n\t\tt.Logf(\"success\")\n\t})\n\tassert.False(t, report.Failed)\n\tassert.False(t, report.Fatally)\n\tassert.Empty(t, report.Errors)\n}\n\nfunc TestWithFake_Failure(t *testing.T) {\n\treport := WithFake(t, func(t T) {\n\t\tt.Errorf(\"great sadness\")\n\t})\n\tassert.True(t, report.Failed)\n\tassert.False(t, report.Fatally)\n\tassert.Equal(t, []string{\"great sadness\"}, report.Errors)\n}\n\nfunc TestWithFake_Fatal(t *testing.T) {\n\treport := WithFake(t, func(t T) {\n\t\tt.FailNow()\n\t})\n\tassert.True(t, report.Failed)\n\tassert.True(t, report.Fatally)\n}\n"
  },
  {
    "path": "docs/internal/test/t.go",
    "content": "// Copyright (c) 2022 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage test\n\n// T is an interface defining a subset of the *testing.T type's API.\ntype T interface {\n\tCleanup(func())\n\tErrorf(string, ...any)\n\tFailNow()\n\tHelper()\n\tLogf(string, ...any)\n\tName() string\n}\n"
  },
  {
    "path": "docs/internal/test/t_test.go",
    "content": "// Copyright (c) 2022 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage test\n\nimport \"testing\"\n\nvar _ T = (*testing.T)(nil)\n"
  },
  {
    "path": "docs/mkdocs.yml",
    "content": "site_name: Fx\nsite_url: https://uber-go.github.io/fx\nsite_description: >-\n  A dependency injection system for Go.\n\nrepo_url: https://github.com/uber-go/fx\nrepo_name: uber-go/fx\nedit_uri: edit/master/docs/src/\n\n# .md files reside inside the src directory.\ndocs_dir: src\n\n# The generated site will be placed in the _site directory.\n# This is the default for GitHub Pages' upload-artifact action.\nsite_dir: _site\n\nextra:\n  analytics:\n    provider: google\n    property: G-4YWLTPJ46M\n\n# Treat all warnings as errors.\nstrict: true\n\n# By default, mkdocs will turn \"foo/bar.md\" into \"foo/bar/index.html\",\n# linking to it as \"foo/bar/\".\n# This does not match what we were using previously (foo/bar.html).\n# So we'll disable this behavior.\nuse_directory_urls: false\n\nvalidation:\n  # Warn about Markdown files not listed in the nav.\n  omitted_files: warn\n\n  # If a link is /foo/bar.md,\n  # turn it into relative to the src/ directory.\n  absolute_links: relative_to_docs\n\n  # Warn about broken internal links to pages or anchors.\n  unrecognized_links: warn\n  anchors: warn\n\ntheme:\n  name: material\n\n  # Support dark and light mode.\n  palette:\n    - scheme: default\n      toggle:\n        icon: material/toggle-switch\n        name: Switch to dark mode\n    - scheme: slate\n      toggle:\n        icon: material/toggle-switch-off-outline\n        name: Switch to light mode\n\n  features:\n    - content.action.edit  # show an 'edit this page' button\n    - content.code.copy    # show 'copy' button on code blocks\n    - content.tooltips     # render alt text as tooltips\n    - header.autohide      # hide header on scroll\n    - navigation.footer    # show next/prev page footer\n    - navigation.indexes   # allow foo/index.md to be home for foo/\n    - navigation.instant   # use SPA-style navigation\n    - navigation.instant.progress\n                           # show loading progress for instant nav\n    - search.suggest       # show search suggestions\n    - toc.follow           # highlight current section in TOC\n    - toc.integrate        # merge TOC into nav sidebar\n\nplugins:\n  # Downloads third-party assets at build time and bundles them with the site.\n  # This avoids calling out to third-party servers when the site is viewed.\n  # We'll do this only if the build is for 'master'\n  - privacy:\n      enabled: !ENV [MASTER_BUILD, false]\n\n  # Enable search\n  - search\n\n  # Show Created/Modified dates\n  - git-revision-date-localized:\n      enabled: !ENV [CI, false]\n      enable_creation_date: true\n      fallback_to_build_date: true\n\n  # Redirect old links to new ones.\n  - redirects:\n      redirect_maps:\n        intro.md: index.md\n        value-groups.md: value-groups/index.md\n\n\nmarkdown_extensions:\n  - admonition        # admonitions (info/warning/error/etc.)\n  - attr_list         # custom HTML attributes for Markdown elements\n  - def_list          # definition lists\n  - md_in_html        # HTML blocks tagged with Markdown contents\n  - pymdownx.details  # collapsible blocks\n\n  # snippets enables including code snippets from other files\n  # with the \"--8<--\" syntax.\n  #\n  # It will search for snippets in the provided base paths.\n  # We put code samples in the \"ex/\" directory, so that's one of the base paths.\n  - pymdownx.snippets:\n      base_path: [ex]\n\n  # Syntax-highlighting of code fences (```),\n  # plus custom fences for Mermaid diagrams.\n  - pymdownx.superfences:\n      # Mermaid diagram support.\n      custom_fences:\n        - name: mermaid\n          class: mermaid\n          format: !!python/name:pymdownx.superfences.fence_code_format\n\n  # Tabbed content blocks, e.g. \"Language A\" vs \"Language B\".\n  - pymdownx.tabbed:\n      alternate_style: true  # recommended\n      slugify: !!python/object/apply:pymdownx.slugs.slugify\n        kwds:\n          case: lower\n\n  # :foo-bar: emoji syntax.\n  #\n  # See https://squidfunk.github.io/mkdocs-material/reference/icons-emojis/\n  # to search through available emojis.\n  # The emojis are rendered into inline svgs at build time.\n  - pymdownx.emoji:\n      emoji_index: !!python/name:material.extensions.emoji.twemoji\n      emoji_generator: !!python/name:material.extensions.emoji.to_svg\n\n  # GitHub-style task lists.\n  - pymdownx.tasklist:\n      custom_checkbox: true  # recommended\n\n  # Generate a TOC for all pages with a permalink for all headers.\n  - toc:\n      permalink: true\n\nnav:\n  - Home: index.md\n  - Get started:\n    - get-started/index.md\n    - get-started/minimal.md\n    - get-started/http-server.md\n    - get-started/echo-handler.md\n    - get-started/logger.md\n    - get-started/registration.md\n    - get-started/another-handler.md\n    - get-started/many-handlers.md\n    - get-started/conclusion.md\n  - Concepts:\n    - Container: container.md\n    - Lifecycle: lifecycle.md\n    - Modules: modules.md\n  - Features:\n    - Parameter Objects: parameter-objects.md\n    - Result Objects: result-objects.md\n    - Annotations: annotate.md\n    - Value groups:\n      - value-groups/index.md\n      - value-groups/feed.md\n      - value-groups/consume.md\n  - FAQ: faq.md\n  - Community:\n    - Contributing: contributing.md\n  - Release notes: changelog.md\n  - API Reference: https://pkg.go.dev/go.uber.org/fx\n\n# Pages that are not listed in the nav must be listed here.\n# not_in_nav: |\n#   get-started/*.md\n\n\n# Also watch ex/ for changes\n# as that's where we store snippets.\nwatch:\n  - ex\n"
  },
  {
    "path": "docs/pyproject.toml",
    "content": "[tool.uv]\ndev-dependencies = [\n    \"mkdocs-material>=9.5.33\",\n    \"mkdocs-git-revision-date-localized-plugin>=1.2.7\",\n    \"mkdocs>=1.6.0\",\n    \"mkdocs-redirects>=1.2.1\",\n]\n[tool.uv.workspace]\nmembers = []\n"
  },
  {
    "path": "docs/src/annotate.md",
    "content": "# Annotations\n\nYou can annotate functions and values with the `fx.Annotate` function\nbefore passing them to\n`fx.Provide`, `fx.Supply`, `fx.Invoke`, `fx.Decorate`, or `fx.Replace`.\n\nThis allows you to re-use a plain Go function to do the following\nwithout manually wrapping the function to use\n[parameter](parameter-objects.md) or [result](result-objects.md) objects.\n\n- [feed values to a value group](value-groups/feed.md#with-annotated-functions)\n- [consume values from a value group](value-groups/consume.md#with-annotated-functions)\n\n<!-- TODO: named values and optional dependencies in the list above -->\n\n## Annotating a function\n\n**Prerequisites**\n\nA function that:\n\n- does not accept a [parameter object](parameter-objects.md), when\n  annotating with `fx.ParamTags`.\n- does not return a [result object](result-objects.md) when annotating\n  with `fx.ResultTags`.\n\n**Steps**\n\n1. Given a function that you're passing to\n   `fx.Provide`, `fx.Invoke`, or `fx.Decorate`,\n\n     ```go\n     --8<-- \"annotate/sample.go:before\"\n     ```\n\n2. Wrap the function with `fx.Annotate`.\n\n     ```go\n     --8<-- \"annotate/sample.go:wrap-1\"\n     --8<-- \"annotate/sample.go:wrap-2\"\n     ```\n\n3. Inside `fx.Annotate`, pass in your annotations.\n\n     ```go\n     --8<-- \"annotate/sample.go:annotate\"\n     ```\n\n     This annotation tags the result of the function with a name.\n\n**Related resources**\n\n- [fx.Annotation](https://pkg.go.dev/go.uber.org/fx#Annotation)\n  holds a list of all supported annotations.\n\n## Casting structs to interfaces\n\nYou can use function annotations to cast a struct value returned by a function\ninto an interface consumed by another function.\n\n**Prerequisites**\n\n1. A function that produces a struct or pointer value.\n\n     ```go\n     --8<-- \"annotate/cast.go:constructor\"\n     ```\n\n2. A function that consumes the result of the producer.\n\n     ```go\n     --8<-- \"annotate/cast_bad.go:struct-consumer\"\n     ```\n\n3. Both functions are provided to the Fx application.\n\n     ```go\n     --8<-- \"annotate/cast_bad.go:provides\"\n     ```\n\n**Steps**\n\n1. Declare an interface that matches the API of the produced `*http.Client`.\n\n     ```go\n     --8<-- \"annotate/cast.go:interface\"\n     ```\n\n2. Change the consumer to accept the interface instead of the struct.\n\n     ```go\n     --8<-- \"annotate/cast.go:iface-consumer\"\n     ```\n\n3. Finally, annotate the producer with `fx.As` to state\n   that it produces an interface value.\n\n     ```go\n     --8<-- \"annotate/cast.go:provides\"\n     ```\n\nWith this change,\n\n- the annotated function now only puts the interface into the container\n- the producer's API remains unchanged\n- the consumer is decoupled from the implementation and independently testable\n"
  },
  {
    "path": "docs/src/container.md",
    "content": "# Container\n\nContainer is the abstraction responsible for holding all constructors and values.\nIt’s the primary means by which an application interacts with Fx.\nYou teach the container about the needs of your application,\nhow to perform certain operations,\nand then you let it handle actually running your application.\n\nFx does not provide direct access to the container.\nInstead, you specify operations to perform on the container\nby providing `fx.Option`s to the `fx.New` constructor.\n\n```go\npackage fx\n\ntype App\n  func New(opts ...Option) *App\n  func (app *App) Run()\n\ntype Option\n  func Provide(constructors ...interface{}) Option\n  func Invoke(funcs ...interface{}) Option\n```\n\nCheck the [API Reference](https://pkg.go.dev/go.uber.org/fx#Option)\nfor a complete list of options and their behaviors.\n\n## Providing values\n\nYou must provide values to the container before you can use them.\nFx provides two ways to provide values to the container:\n\n- `fx.Provide` for values that have a constructor.\n\n    ```go\n    fx.Provide(\n      func(cfg *Config) *Logger { /* ... */ },\n    )\n    ```\n\n    This says that Fx should use this function to construct a `*Logger`,\n    and that a `*Config` is required to build one.\n\n- `fx.Supply` for pre-built non-interface values.\n\n    ```go\n    fx.Provide(\n      fx.Supply(&Config{\n        Name: \"my-app\",\n      }),\n    )\n    ```\n\n    This says that Fx should use the provided `*Config` as-is.\n\n    **Important**: `fx.Supply` is only for non-interface values.\n    See *When to use fx.Supply* for more details.\n\nValues provided to the container are available to all other constructors.\nIn the example above, the `*Config` would become available to the `*Logger` constructor,\nand the `*Logger` to any other constructors that need it.\n\n### When to use fx.Supply\n\nUsually, `fx.Provide` is the right choice because more often than not,\nconstructing an object requires its dependencies.\n`fx.Supply` is a convenience function for the rare cases where that isn't true:\nstandalone values that don't depend on anything else.\n\n```go\nfx.Provide(func() *Config { return &Config{Name: \"my-app\"} })\n// is the same as\nfx.Supply(&Config{Name: \"my-app\"})\n```\n\nHowever, even then, `fx.Supply` comes with a caveat:\nit can only be used for non-interface values.\n\n??? question \"Why can't I use fx.Supply for interface values?\"\n\n    This is a technical limitation imposed by the fact that `fx.Supply` has to rely\n    on runtime reflection to determine the type of the value.\n\n    Passing an interface value to `fx.Supply` is a lossy operation:\n    it loses the original interface type, only giving us `interface{}`,\n    at which point reflection will only reveal the concrete type of the value.\n\n    For example, consider:\n\n    ```go\n    var svc RepositoryService = &repoService{ ... }\n    ```\n\n    If you were to pass `svc` to `fx.Supply`,\n    the container would only know that it's a `*repoService`,\n    and it will not know that you intend to use it as a `RepositoryService`.\n\n## Using values\n\nProviding values to the container only makes them available to the application.\nIt doesn't do anything with them yet.\nConstructors passed to `fx.Provide` are not called until they are needed.\n\nFor example, the following won't do anything:\n\n```go\nfx.New(\n  fx.Provide(newHTTPServer), // provides an *http.Server\n).Run()\n```\n\nYou next have to tell the container what is needed, and what to do with it.\nFx provides [`fx.Invoke`](https://pkg.go.dev/go.uber.org/fx#Invoke) for this purpose.\n\nIn the example above, we'll want an invocation that starts the server:\n\n```go\nfx.New(\n  fx.Provide(newHTTPServer),\n  fx.Invoke(startHTTPServer),\n).Run()\n```\n\n### When to use fx.Invoke\n\n`fx.Invoke` is typically used for root-level invocations,\nlike starting a server or running a main loop.\nIt's also useful for invoking functions that have side effects.\n\nExamples of cases where you might use `fx.Invoke`:\n\n- Starting a background worker\n- Configuring a global logger\n\nAs an example, consider an application organized into many distinct abstractions.\n\n```mermaid\nflowchart LR\n  CacheWarmer --> Redis\n  Server[http.Server] --> UserHandler & PostHandler\n  UserHandler --> Redis[redis.Client] & Client[http.Client]\n  PostHandler --> sqlDB[sql.DB]\n\n  subgraph Roots\n    CacheWarmer\n    Server\n  end\n```\n\n`CacheWarmer` and `http.Server` are the roots of the application.\nWe'll need `fx.Invoke` for the side effects of starting the server\nand the cache warmer loop.\nEverything else will be handled by the container automatically.\n"
  },
  {
    "path": "docs/src/faq.md",
    "content": "# Frequently Asked Questions\n\nThis page contains answers to common questions and issues with using Fx.\n\n## Does the order of `fx.Option`s matter?\n\nNo, the order in which you provide Fx options\nto `fx.Options`, `fx.New`, `fx.Module`, and others does not matter.\n\nOrdering of options relative to each other is as follows:\n\n* Adding values:\n  Operations like `fx.Provide` and `fx.Supply` are run in dependency order.\n  Dependencies are determined by the function parameters and results.\n\n    ```go\n    // The following are all equivalent:\n    fx.Options(fx.Provide(ParseConfig, NewLogger))\n    fx.Options(fx.Provide(NewLogger, ParseConfig))\n    fx.Options(fx.Provide(ParseConfig), fx.Provide(NewLogger))\n    fx.Options(fx.Provide(NewLogger), fx.Provide(ParseConfig))\n    ```\n\n* Consuming values:\n  Operations like `fx.Invoke` and `fx.Populate` are run\n  after their dependencies have been satisfied: after `fx.Provide`s.\n\n    Relative to each other, invokes are run in the order they were specified.\n\n    ```go\n    fx.Invoke(a, b)\n    // a() is run before b()\n    ```\n\n    `fx.Module` hierarchies affect invocation order:\n    invocations in a parent module are run after those of a child module.\n\n    ```go\n    fx.Options(\n      fx.Invoke(a),\n      fx.Module(\"child\", fx.Invoke(b)),\n    ),\n    // b() is run before a()\n    ```\n\n* Replacing values:\n  Operations like `fx.Decorate` and `fx.Replace` are run\n  after the Provide operations that they depend on,\n  but before the Invoke operations that consume those values.\n\n    Ordering of decorations relative to each other\n    is determined by `fx.Module` hierarchies:\n    decorations in a parent module are applied after those of a child module.\n\n## Why does `fx.Supply` not accept interfaces?\n\nThis is a technical limitation of how reflection in Go works.\nSuppose you have:\n\n```go\nvar redisClient ClientInterface = &redis.Client{ ... }\n```\n\nWhen you call `fx.Supply(redisClient)`,\nthe knowledge that you intended to use this as a `ClientInterface` is lost.\nFx has to use runtime reflection to inspect the type of the value,\nand at that point the Go runtime only tells it that it’s a `*redis.Client`.\n\nYou can work around this with the `fx.Annotate` function\nand the `fx.As` annotation.\n\n```go\nfx.Supply(\n  fx.Annotate(redisClient, fx.As(new(ClientInterface))),\n)\n```\n"
  },
  {
    "path": "docs/src/get-started/another-handler.md",
    "content": "# Register another handler\n\nThe handler we defined above has a single handler.\nLet's add another.\n\n1. Build a new handler in the same file.\n\n     ```go\n     --8<-- \"get-started/06-another-handler/main.go:hello-init\"\n     ```\n\n2. Implement the `Route` interface for this handler.\n\n     ```go\n     --8<-- \"get-started/06-another-handler/main.go:hello-methods-1\"\n     --8<-- \"get-started/06-another-handler/main.go:hello-methods-2\"\n     ```\n\n     The handler reads its request body,\n     and writes a welcome message back to the caller.\n\n3. Provide this to the application as a `Route` next to `NewEchoHandler`.\n\n     ```go\n     --8<-- \"get-started/06-another-handler/main.go:hello-provide-partial-1\"\n     --8<-- \"get-started/06-another-handler/main.go:hello-provide-partial-2\"\n     --8<-- \"get-started/06-another-handler/main.go:hello-provide-partial-3\"\n     ```\n\n4. Run the application--the service will fail to start.\n\n     ```\n     [Fx] PROVIDE    *http.Server <= main.NewHTTPServer()\n     [Fx] PROVIDE    *http.ServeMux <= main.NewServeMux()\n     [Fx] PROVIDE    main.Route <= fx.Annotate(main.NewEchoHandler(), fx.As([[main.Route]])\n     [Fx] Error after options were applied: fx.Provide(fx.Annotate(main.NewHelloHandler(), fx.As([[main.Route]])) from:\n     [...]\n     [Fx] ERROR              Failed to start: the following errors occurred:\n      -  fx.Provide(fx.Annotate(main.NewHelloHandler(), fx.As([[main.Route]])) from:\n         [...]\n         Failed: cannot provide function \"main\".NewHelloHandler ([..]/main.go:53): cannot provide main.Route from [0].Field0: already provided by \"main\".NewEchoHandler ([..]/main.go:80)\n     ```\n\n     That's a lot of output, but inside the error message, we see:\n\n     ```\n     cannot provide main.Route from [0].Field0: already provided by \"main\".NewEchoHandler ([..]/main.go:80)\n     ```\n\n     This fails because Fx does not allow two instances of the same type\n     to be present in the container without annotating them.\n     `NewServeMux` does not know which `Route` to use. Let's fix this.\n\n5. Annotate `NewEchoHandler` and `NewHelloHandler` in `main()` with names for\n   both handlers.\n\n     ```go\n     --8<-- \"get-started/06-another-handler/main.go:route-provides\"\n     ```\n\n6. Add another Route parameter to `NewServeMux`.\n\n     ```go\n     --8<-- \"get-started/06-another-handler/main.go:mux\"\n     ```\n\n7. Annotate `NewServeMux` in `main()` to pick these two *names values*.\n\n     ```go\n     --8<-- \"get-started/06-another-handler/main.go:mux-provide\"\n     ```\n\n8. Run the program.\n\n     ```\n     {\"level\":\"info\",\"msg\":\"provided\",\"constructor\":\"main.NewHTTPServer()\",\"type\":\"*http.Server\"}\n     {\"level\":\"info\",\"msg\":\"provided\",\"constructor\":\"fx.Annotate(main.NewServeMux(), fx.ParamTags([\\\"name:\\\\\\\"echo\\\\\\\"\\\" \\\"name:\\\\\\\"hello\\\\\\\"\\\"])\",\"type\":\"*http.ServeMux\"}\n     {\"level\":\"info\",\"msg\":\"provided\",\"constructor\":\"fx.Annotate(main.NewEchoHandler(), fx.ResultTags([\\\"name:\\\\\\\"echo\\\\\\\"\\\"]), fx.As([[main.Route]])\",\"type\":\"main.Route[name = \\\"echo\\\"]\"}\n     {\"level\":\"info\",\"msg\":\"provided\",\"constructor\":\"fx.Annotate(main.NewHelloHandler(), fx.ResultTags([\\\"name:\\\\\\\"hello\\\\\\\"\\\"]), fx.As([[main.Route]])\",\"type\":\"main.Route[name = \\\"hello\\\"]\"}\n     {\"level\":\"info\",\"msg\":\"provided\",\"constructor\":\"go.uber.org/zap.NewExample()\",\"type\":\"*zap.Logger\"}\n     {\"level\":\"info\",\"msg\":\"provided\",\"constructor\":\"go.uber.org/fx.New.func1()\",\"type\":\"fx.Lifecycle\"}\n     {\"level\":\"info\",\"msg\":\"provided\",\"constructor\":\"go.uber.org/fx.(*App).shutdowner-fm()\",\"type\":\"fx.Shutdowner\"}\n     {\"level\":\"info\",\"msg\":\"provided\",\"constructor\":\"go.uber.org/fx.(*App).dotGraph-fm()\",\"type\":\"fx.DotGraph\"}\n     {\"level\":\"info\",\"msg\":\"initialized custom fxevent.Logger\",\"function\":\"main.main.func1()\"}\n     {\"level\":\"info\",\"msg\":\"invoking\",\"function\":\"main.main.func2()\"}\n     {\"level\":\"info\",\"msg\":\"OnStart hook executing\",\"callee\":\"main.NewHTTPServer.func1()\",\"caller\":\"main.NewHTTPServer\"}\n     {\"level\":\"info\",\"msg\":\"Starting HTTP server\",\"addr\":\":8080\"}\n     {\"level\":\"info\",\"msg\":\"OnStart hook executed\",\"callee\":\"main.NewHTTPServer.func1()\",\"caller\":\"main.NewHTTPServer\",\"runtime\":\"56.334µs\"}\n     {\"level\":\"info\",\"msg\":\"started\"}\n     ```\n\n9. Send requests to it.\n\n     ```\n     $ curl -X POST -d 'hello' http://localhost:8080/echo\n     hello\n\n     $ curl -X POST -d 'gopher' http://localhost:8080/hello\n     Hello, gopher\n     ```\n\n**What did we just do?**\n\nWe added a constructor that produces a value\nwith the same type as an existing type.\nWe annotated constructors with `fx.ResultTags` to produce *named values*,\nand the consumer with `fx.ParamTags` to consume these named values.\n"
  },
  {
    "path": "docs/src/get-started/conclusion.md",
    "content": "# Conclusion\n\nThis marks the end of this tutorial.\nIn this tutorial, we covered,\n\n- [x] how to start an Fx application from scratch\n- [x] how to inject new dependencies and modify existing ones\n- [x] how to use interfaces to decouple components\n- [x] how to use named values\n- [x] how to use [value groups](/value-groups/index.md)\n"
  },
  {
    "path": "docs/src/get-started/echo-handler.md",
    "content": "# Register a handler\n\nWe built a server that can receive requests,\nbut it doesn't yet know how to handle them.\nLet's fix that.\n\n1. Define a basic HTTP handler that copies the incoming request body\n   to the response.\n   Add the following to the bottom of your file.\n\n     ```go\n     --8<-- \"get-started/03-echo-handler/main.go:echo-handler\"\n     ```\n\n     Provide this to the application.\n\n     ```go\n     --8<-- \"get-started/03-echo-handler/main.go:provide-handler-1\"\n     --8<-- \"get-started/03-echo-handler/main.go:provide-handler-2\"\n     ```\n\n2. Next, write a function that builds an `*http.ServeMux`.\n   The `*http.ServeMux` will route requests received by the server to different\n   handlers.\n   To begin with, it will route requests sent to `/echo` to `*EchoHandler`,\n   so its constructor should accept `*EchoHandler` as an argument.\n\n     ```go\n     --8<-- \"get-started/03-echo-handler/main.go:serve-mux\"\n     ```\n\n     Likewise, provide this to the application.\n\n     ```go\n     --8<-- \"get-started/03-echo-handler/main.go:provides\"\n     ```\n\n     Note that `NewServeMux` was added above `NewEchoHandler`--the order\n     in which constructors are given to `fx.Provide` does not matter.\n\n3. Lastly, modify the `NewHTTPServer` function to connect\n   the server to this `*ServeMux`.\n\n     ```go\n     --8<-- \"get-started/03-echo-handler/main.go:connect-mux\"\n     ```\n\n4. Run the server.\n\n     ```\n     [Fx] PROVIDE    *http.Server <= main.NewHTTPServer()\n     [Fx] PROVIDE    *http.ServeMux <= main.NewServeMux()\n     [Fx] PROVIDE    *main.EchoHandler <= main.NewEchoHandler()\n     [Fx] PROVIDE    fx.Lifecycle <= go.uber.org/fx.New.func1()\n     [Fx] PROVIDE    fx.Shutdowner <= go.uber.org/fx.(*App).shutdowner-fm()\n     [Fx] PROVIDE    fx.DotGraph <= go.uber.org/fx.(*App).dotGraph-fm()\n     [Fx] INVOKE             main.main.func1()\n     [Fx] HOOK OnStart               main.NewHTTPServer.func1() executing (caller: main.NewHTTPServer)\n     Starting HTTP server at :8080\n     [Fx] HOOK OnStart               main.NewHTTPServer.func1() called by main.NewHTTPServer ran successfully in 7.459µs\n     [Fx] RUNNING\n     ```\n\n5. Send a request to the server.\n\n     ```shell\n     $ curl -X POST -d 'hello' http://localhost:8080/echo\n     hello\n     ```\n\n**What did we just do?**\n\nWe added more components with `fx.Provide`.\nThese components declared dependencies on each other\nby adding parameters to their constructors.\nFx will resolve component dependencies by parameters and return values\nof the provided functions.\n"
  },
  {
    "path": "docs/src/get-started/http-server.md",
    "content": "# Add an HTTP server\n\nIn the previous section, we wrote a minimal Fx application\nthat doesn't do anything.\nLet's add an HTTP server to it.\n\n1. Write a function to build your HTTP server.\n\n     ```go\n     --8<-- \"get-started/02-http-server/main.go:partial-1\"\n     --8<-- \"get-started/02-http-server/main.go:partial-2\"\n     ```\n\n     This isn't enough, though--we need to tell Fx how to start the HTTP server.\n     That's what the additional `fx.Lifecycle` argument is for.\n\n2. Add a *lifecycle hook* to the application with the `fx.Lifecycle` object.\n   This tells Fx how to start and stop the HTTP server.\n\n     ```go\n     --8<-- \"get-started/02-http-server/main.go:full\"\n     ```\n\n3. Provide this to your Fx application above with `fx.Provide`.\n\n     ```go\n     --8<-- \"get-started/02-http-server/main.go:provide-server-1\"\n     --8<-- \"get-started/02-http-server/main.go:provide-server-2\"\n     ```\n\n4. Run the application.\n\n     ```\n     [Fx] PROVIDE    *http.Server <= main.NewHTTPServer()\n     [Fx] PROVIDE    fx.Lifecycle <= go.uber.org/fx.New.func1()\n     [Fx] PROVIDE    fx.Shutdowner <= go.uber.org/fx.(*App).shutdowner-fm()\n     [Fx] PROVIDE    fx.DotGraph <= go.uber.org/fx.(*App).dotGraph-fm()\n     [Fx] RUNNING\n     ```\n\n     Huh? Did something go wrong?\n     The first line in the output states that the server was provided,\n     but it doesn't include our \"Starting HTTP server\" message.\n     The server didn't run.\n\n5. To fix that, add an `fx.Invoke` that requests the constructed server.\n\n     ```go\n     --8<-- \"get-started/02-http-server/main.go:app\"\n     ```\n\n6. Run the application again.\n   This time we should see \"Starting HTTP server\" in the output.\n\n     ```\n     [Fx] PROVIDE    *http.Server <= main.NewHTTPServer()\n     [Fx] PROVIDE    fx.Lifecycle <= go.uber.org/fx.New.func1()\n     [Fx] PROVIDE    fx.Shutdowner <= go.uber.org/fx.(*App).shutdowner-fm()\n     [Fx] PROVIDE    fx.DotGraph <= go.uber.org/fx.(*App).dotGraph-fm()\n     [Fx] INVOKE             main.main.func1()\n     [Fx] HOOK OnStart               main.NewHTTPServer.func1() executing (caller: main.NewHTTPServer)\n     Starting HTTP server at :8080\n     [Fx] HOOK OnStart               main.NewHTTPServer.func1() called by main.NewHTTPServer ran successfully in 7.958µs\n     [Fx] RUNNING\n     ```\n\n7. Send a request to the running server.\n\n     ```shell\n     $ curl http://localhost:8080\n     404 page not found\n     ```\n\n     The request is a 404 because the server doesn't know how to handle it yet.\n     We'll fix that in the next section.\n\n8. Stop the application.\n\n     ```\n     ^C\n     [Fx] INTERRUPT\n     [Fx] HOOK OnStop                main.NewHTTPServer.func2() executing (caller: main.NewHTTPServer)\n     [Fx] HOOK OnStop                main.NewHTTPServer.func2() called by main.NewHTTPServer ran successfully in 129.875µs\n     ```\n\n**What did we just do?**\n\nWe used `fx.Provide` to add an HTTP server to the application.\nThe server hooks into the Fx application lifecycle--it will\nstart serving requests when we call `App.Run`,\nand it will stop running when the application receives a stop signal.\nWe used `fx.Invoke` to request that the HTTP server is always instantiated,\neven if none of the other components in the application reference it directly.\n\n**Related Resources**\n\n* [Application lifecycle](/lifecycle.md) further explains what Fx lifecycles are,\n  and how to use them.\n\n<!-- \nTODO: when the docs exist\n\n**Related Resources**\n\n* TODO: link to fx.Provide\n* TODO: link to fx.Invoke\n\n-->\n\n"
  },
  {
    "path": "docs/src/get-started/index.md",
    "content": "# Get started with Fx\n\n\nThis introduces you to the basics of Fx.\nIn this tutorial you will:\n\n- [start an empty application](minimal.md)\n- [add an HTTP server to it](http-server.md)\n- [register a handler with the server](echo-handler.md)\n- [add logging to your application](logger.md)\n- [refactor to loosen coupling to your handler](registration.md)\n- [add another handler to the server](another-handler.md)\n- [generalize your implementation](many-handlers.md)\n\nFirst, get set up for the rest of the tutorial.\n\n1. Start a new empty project.\n\n   ```bash\n   mkdir fxdemo\n   cd fxdemo\n   go mod init example.com/fxdemo\n   ```\n\n2. Install the latest version of Fx.\n\n   ```bash\n   go get go.uber.org/fx@latest\n   ```\n\nNow begin by [creating a minimal application](minimal.md).\n"
  },
  {
    "path": "docs/src/get-started/logger.md",
    "content": "# Add a logger\n\nOur application currently prints\nthe \"Starting HTTP server\" message to standard out,\nand errors to standard error.\nBoth, standard out and error are also a form of global state.\nWe should print to a logger object.\n\nWe'll use [Zap](https://pkg.go.dev/go.uber.org/zap) in this section of the tutorial\nbut you should be able to use any logging system.\n\n1. Provide a Zap logger to the application.\n   In this tutorial, we'll use [`zap.NewExample`](https://pkg.go.dev/go.uber.org/zap#NewExample),\n   but for real applications, you should use `zap.NewProduction`\n   or build a more customized logger.\n\n     ```go\n     --8<-- \"get-started/04-logger/main.go:provides\"\n     ```\n\n2. Add a field to hold the logger on `EchoHandler`,\n   and in `NewEchoHandler` add a new logger argument to set this field.\n\n     ```go\n     --8<-- \"get-started/04-logger/main.go:echo-init-1\"\n     --8<-- \"get-started/04-logger/main.go:echo-init-2\"\n     ```\n\n3. In the `EchoHandler.ServeHTTP` method,\n   use the logger instead of printing to standard error.\n\n     ```go\n     --8<-- \"get-started/04-logger/main.go:echo-serve\"\n     ```\n\n4. Similarly, update `NewHTTPServer` to expect a logger\n   and log the \"Starting HTTP server\" message to that.\n\n     ```go\n     --8<-- \"get-started/04-logger/main.go:http-server\"\n     ```\n\n5. (**Optional**) You can use the same Zap logger for Fx's own logs as well.\n\n     ```go\n     --8<-- \"get-started/04-logger/main.go:fx-logger\"\n     ```\n\n     This will replace the `[Fx]` messages with messages printed to the logger.\n\n6. Run the application.\n\n     ```\n     {\"level\":\"info\",\"msg\":\"provided\",\"constructor\":\"main.NewHTTPServer()\",\"type\":\"*http.Server\"}\n     {\"level\":\"info\",\"msg\":\"provided\",\"constructor\":\"main.NewServeMux()\",\"type\":\"*http.ServeMux\"}\n     {\"level\":\"info\",\"msg\":\"provided\",\"constructor\":\"main.NewEchoHandler()\",\"type\":\"*main.EchoHandler\"}\n     {\"level\":\"info\",\"msg\":\"provided\",\"constructor\":\"go.uber.org/zap.NewExample()\",\"type\":\"*zap.Logger\"}\n     {\"level\":\"info\",\"msg\":\"provided\",\"constructor\":\"go.uber.org/fx.New.func1()\",\"type\":\"fx.Lifecycle\"}\n     {\"level\":\"info\",\"msg\":\"provided\",\"constructor\":\"go.uber.org/fx.(*App).shutdowner-fm()\",\"type\":\"fx.Shutdowner\"}\n     {\"level\":\"info\",\"msg\":\"provided\",\"constructor\":\"go.uber.org/fx.(*App).dotGraph-fm()\",\"type\":\"fx.DotGraph\"}\n     {\"level\":\"info\",\"msg\":\"initialized custom fxevent.Logger\",\"function\":\"main.main.func1()\"}\n     {\"level\":\"info\",\"msg\":\"invoking\",\"function\":\"main.main.func2()\"}\n     {\"level\":\"info\",\"msg\":\"OnStart hook executing\",\"callee\":\"main.NewHTTPServer.func1()\",\"caller\":\"main.NewHTTPServer\"}\n     {\"level\":\"info\",\"msg\":\"Starting HTTP server\",\"addr\":\":8080\"}\n     {\"level\":\"info\",\"msg\":\"OnStart hook executed\",\"callee\":\"main.NewHTTPServer.func1()\",\"caller\":\"main.NewHTTPServer\",\"runtime\":\"6.292µs\"}\n     {\"level\":\"info\",\"msg\":\"started\"}\n     ```\n\n7. Post a request to it.\n\n     ```shell\n     $ curl -X POST -d 'hello' http://localhost:8080/echo\n     hello\n     ```\n\n**What did we just do?**\n\nWe added another component to the application with `fx.Provide`,\nand injected that into other components that need to print messages.\nTo do that, we only had to add a new parameter to the constructors.\n\nIn the optional step,\nwe told Fx that we'd like to provide a custom logger for Fx's own operations.\nWe used the existing `fxevent.ZapLogger` to build this custom logger from our\ninjected logger, so that all logs follow the same format.\n"
  },
  {
    "path": "docs/src/get-started/many-handlers.md",
    "content": "# Register many handlers\n\nWe added two handlers in the previous section,\nbut we reference them both explicitly by name when we build `NewServeMux`.\nThis will quickly become inconvenient if we add more handlers.\n\nIt's preferable if `NewServeMux` doesn't know how many handlers or their names,\nand instead just accepts a list of handlers to register.\n\nLet's do that.\n\n1. Modify `NewServeMux` to operate on a list of `Route` objects.\n\n     ```go\n     --8<-- \"get-started/07-many-handlers/main.go:mux\"\n     ```\n\n2. Annotate the `NewServeMux` entry in `main` to say\n   that it accepts a slice that contains the contents of the \"routes\" group.\n\n     ```go\n     --8<-- \"get-started/07-many-handlers/main.go:mux-provide\"\n     ```\n\n3. Define a new function `AsRoute` to build functions that feed into this\n   group.\n\n     ```go\n     --8<-- \"get-started/07-many-handlers/main.go:AsRoute\"\n     ```\n\n4. Wrap the `NewEchoHandler` and `NewHelloHandler` constructors in `main()`\n   with `AsRoute` so that they feed their routes into this group.\n\n     ```go\n     --8<-- \"get-started/07-many-handlers/main.go:route-provides-1\"\n     --8<-- \"get-started/07-many-handlers/main.go:route-provides-2\"\n     ```\n\n5. Finally, run the application.\n\n     ```\n     {\"level\":\"info\",\"msg\":\"provided\",\"constructor\":\"main.NewHTTPServer()\",\"type\":\"*http.Server\"}\n     {\"level\":\"info\",\"msg\":\"provided\",\"constructor\":\"fx.Annotate(main.NewServeMux(), fx.ParamTags([\\\"group:\\\\\\\"routes\\\\\\\"\\\"])\",\"type\":\"*http.ServeMux\"}\n     {\"level\":\"info\",\"msg\":\"provided\",\"constructor\":\"fx.Annotate(main.NewEchoHandler(), fx.ResultTags([\\\"group:\\\\\\\"routes\\\\\\\"\\\"]), fx.As([[main.Route]])\",\"type\":\"main.Route[group = \\\"routes\\\"]\"}\n     {\"level\":\"info\",\"msg\":\"provided\",\"constructor\":\"fx.Annotate(main.NewHelloHandler(), fx.ResultTags([\\\"group:\\\\\\\"routes\\\\\\\"\\\"]), fx.As([[main.Route]])\",\"type\":\"main.Route[group = \\\"routes\\\"]\"}\n     {\"level\":\"info\",\"msg\":\"provided\",\"constructor\":\"go.uber.org/zap.NewExample()\",\"type\":\"*zap.Logger\"}\n     {\"level\":\"info\",\"msg\":\"provided\",\"constructor\":\"go.uber.org/fx.New.func1()\",\"type\":\"fx.Lifecycle\"}\n     {\"level\":\"info\",\"msg\":\"provided\",\"constructor\":\"go.uber.org/fx.(*App).shutdowner-fm()\",\"type\":\"fx.Shutdowner\"}\n     {\"level\":\"info\",\"msg\":\"provided\",\"constructor\":\"go.uber.org/fx.(*App).dotGraph-fm()\",\"type\":\"fx.DotGraph\"}\n     {\"level\":\"info\",\"msg\":\"initialized custom fxevent.Logger\",\"function\":\"main.main.func1()\"}\n     {\"level\":\"info\",\"msg\":\"invoking\",\"function\":\"main.main.func2()\"}\n     {\"level\":\"info\",\"msg\":\"OnStart hook executing\",\"callee\":\"main.NewHTTPServer.func1()\",\"caller\":\"main.NewHTTPServer\"}\n     {\"level\":\"info\",\"msg\":\"Starting HTTP server\",\"addr\":\":8080\"}\n     {\"level\":\"info\",\"msg\":\"OnStart hook executed\",\"callee\":\"main.NewHTTPServer.func1()\",\"caller\":\"main.NewHTTPServer\",\"runtime\":\"5µs\"}\n     {\"level\":\"info\",\"msg\":\"started\"}\n     ```\n\n6. Send requests to it.\n\n     ```\n     $ curl -X POST -d 'hello' http://localhost:8080/echo\n     hello\n\n     $ curl -X POST -d 'gopher' http://localhost:8080/hello\n     Hello, gopher\n     ```\n\n**What did we just do?**\n\nWe annotated `NewServeMux` to consume a *value group* as a slice,\nand we annotated our existing handler constructors to feed into this value\ngroup.\nAny other constructor in the application can also feed values\ninto this value group as long as the result conforms to the `Route` interface.\nThey will all be collected together and passed into our `ServeMux` constructor.\n\n**Related Resources**\n\n* [Value groups](/value-groups/index.md) further explains what value groups are,\n  and how to use them.\n"
  },
  {
    "path": "docs/src/get-started/minimal.md",
    "content": "# Create a minimal application\n\nLet's build the hello-world equivalent of an Fx application.\nThis application won't do anything yet except print a bunch of logs.\n\n1. Write a minimal `main.go`.\n\n   ```go\n   --8<-- \"get-started/01-minimal/main.go:main\"\n   ```\n\n2. Run the application.\n\n   ```bash\n   go run .\n   ```\n\n   You'll see output similar to the following.\n\n   ```\n   [Fx] PROVIDE    fx.Lifecycle <= go.uber.org/fx.New.func1()\n   [Fx] PROVIDE    fx.Shutdowner <= go.uber.org/fx.(*App).shutdowner-fm()\n   [Fx] PROVIDE    fx.DotGraph <= go.uber.org/fx.(*App).dotGraph-fm()\n   [Fx] RUNNING\n   ```\n\n   This shows the default objects provided to the Fx application,\n   but it doesn't do anything meaningful yet.\n   Stop the application with `Ctrl-C`.\n\n   ```\n   [Fx] RUNNING\n   ^C\n   [Fx] INTERRUPT\n   ```\n\n**What did we just do?**\n\nWe built an empty Fx application by calling `fx.New` with no arguments.\nApplications will normally pass arguments to `fx.New` to set up their\ncomponents.\n\nWe then run this application with the `App.Run` method.\nThis method blocks until it receives a signal to stop,\nand it then runs any cleanup operations necessary before exiting.\n\nFx is primarily intended for long-running server applications;\nthese applications typically receive a signal from the deployment system\nwhen it's time to shut down.\n"
  },
  {
    "path": "docs/src/get-started/registration.md",
    "content": "# Decouple registration\n\n`NewServeMux` above declares an explicit dependency on `EchoHandler`.\nThis is an unnecessarily tight coupling.\nDoes the `ServeMux` really need to know the *exact* handler implementation?\nIf we want to write tests for `ServeMux`,\nwe shouldn't have to construct an `EchoHandler`.\n\nLet's try to fix this.\n\n1. Define a `Route` type in your main.go.\n   This is an extension of `http.Handler` where the handler knows its\n   registration path.\n\n     ```go\n     --8<-- \"get-started/05-registration/main.go:route\"\n     ```\n\n2. Modify `EchoHandler` to implement this interface.\n\n     ```go\n     --8<-- \"get-started/05-registration/main.go:echo-pattern\"\n     ```\n\n3. In `main()`, annotate the `NewEchoHandler` entry to state that the handler\n   should be provided as a Route.\n\n     ```go\n     --8<-- \"get-started/05-registration/main.go:provides\"\n     ```\n\n4. Modify `NewServeMux` to accept a Route and use its provided pattern.\n\n     ```go\n     --8<-- \"get-started/05-registration/main.go:mux\"\n     ```\n\n5. Run the service.\n\n     ```\n     {\"level\":\"info\",\"msg\":\"provided\",\"constructor\":\"main.NewHTTPServer()\",\"type\":\"*http.Server\"}\n     {\"level\":\"info\",\"msg\":\"provided\",\"constructor\":\"main.NewServeMux()\",\"type\":\"*http.ServeMux\"}\n     {\"level\":\"info\",\"msg\":\"provided\",\"constructor\":\"fx.Annotate(main.NewEchoHandler(), fx.As([[main.Route]])\",\"type\":\"main.Route\"}\n     {\"level\":\"info\",\"msg\":\"provided\",\"constructor\":\"go.uber.org/zap.NewExample()\",\"type\":\"*zap.Logger\"}\n     {\"level\":\"info\",\"msg\":\"provided\",\"constructor\":\"go.uber.org/fx.New.func1()\",\"type\":\"fx.Lifecycle\"}\n     {\"level\":\"info\",\"msg\":\"provided\",\"constructor\":\"go.uber.org/fx.(*App).shutdowner-fm()\",\"type\":\"fx.Shutdowner\"}\n     {\"level\":\"info\",\"msg\":\"provided\",\"constructor\":\"go.uber.org/fx.(*App).dotGraph-fm()\",\"type\":\"fx.DotGraph\"}\n     {\"level\":\"info\",\"msg\":\"initialized custom fxevent.Logger\",\"function\":\"main.main.func1()\"}\n     {\"level\":\"info\",\"msg\":\"invoking\",\"function\":\"main.main.func2()\"}\n     {\"level\":\"info\",\"msg\":\"OnStart hook executing\",\"callee\":\"main.NewHTTPServer.func1()\",\"caller\":\"main.NewHTTPServer\"}\n     {\"level\":\"info\",\"msg\":\"Starting HTTP server\",\"addr\":\":8080\"}\n     {\"level\":\"info\",\"msg\":\"OnStart hook executed\",\"callee\":\"main.NewHTTPServer.func1()\",\"caller\":\"main.NewHTTPServer\",\"runtime\":\"10.125µs\"}\n     {\"level\":\"info\",\"msg\":\"started\"}\n     ```\n\n6. Send a request to it.\n\n     ```shell\n     $ curl -X POST -d 'hello' http://localhost:8080/echo\n     hello\n     ```\n\n**What did we just do?**\n\nWe introduced an interface to decouple the implementation\nfrom the consumer.\nWe then [annotated](../annotate.md) a previously provided constructor\nwith `fx.Annotate` and `fx.As`\nto [cast its result to that interface](../annotate.md#casting-structs-to-interfaces).\nThis way, `NewEchoHandler` was able to continue returning an `*EchoHandler`.\n"
  },
  {
    "path": "docs/src/index.md",
    "content": "# Fx\n\nFx is **a dependency injection system for Go**.\n\n<div class=\"grid cards\" markdown>\n\n- **Eliminate globals**\n\n    ---\n\n    By using Fx-managed singletons,\n    you can eliminate global state from your application.\n    With Fx, you don't have to rely on `init()` functions for setup,\n    instead relying on Fx to manage the lifecycle of your application.\n\n- **Reduce boilerplate**\n\n    ---\n\n    Fx reduces the amount of code copy-pasted across your services.\n    It lets you define shared application setup in a single place,\n    and then reuse it across all your services.\n\n- **Automatic plumbing**\n\n    ---\n\n    Fx automatically constructs your application's dependency graph.\n    A component added to the application can be used by any other component\n    without any additional configuration.\n\n    [Learn more about the dependency container :material-arrow-right:](container.md)\n\n- **Code reuse**\n\n    ---\n\n    Fx lets teams within your organization build loosely-coupled\n    and well-integrated shareable components referred to as modules.\n\n    [Learn more about modules :material-arrow-right:](modules.md)\n\n- **Battle-tested**\n\n    Fx is the backbone of nearly all Go services at Uber.\n\n</div>\n\n[Get started :material-arrow-right-bold:](get-started/index.md){ .md-button .md-button--primary }\n"
  },
  {
    "path": "docs/src/lifecycle.md",
    "content": "# Application lifecycle\n\nThe lifecycle of an Fx application has two high-level phases:\n*initialization* and *execution*.\nBoth of these, in turn are comprised of multiple steps.\n\nDuring **initialization**, Fx will,\n\n- register all constructors passed to `fx.Provide`\n- register all decorators passed to `fx.Decorate`\n- run all functions passed to `fx.Invoke`,\n  calling constructors and decorators as needed\n\nDuring **execution**, Fx will,\n\n- run all startup hooks appended to the application\n  by providers, decorators, and invoked functions\n- wait for a signal to stop running\n- run all shutdown hooks appended to the application\n\n```mermaid\nflowchart LR\n    subgraph \"Initialization (fx.New)\"\n        Provide --> Decorate --> Invoke\n    end\n    subgraph \"Execution (fx.App.Run)\"\n        Start --> Wait --> Stop\n    end\n    Invoke --> Start\n\n    style Wait stroke-dasharray: 5 5\n```\n\n## Lifecycle hooks\n\nLifecycle hooks provide the ability to schedule work to be executed by Fx,\nwhen the application starts up or shuts down.\nFx provides two kinds of hooks:\n\n- *Startup hooks*, also referred to as `OnStart` hooks.\n  These run in the order they were appended.\n- *Shutdown hooks*, also referred to as `OnStop` hooks.\n  These run in the **reverse** of the order they were appended.\n\nTypically, components that provide a startup hook\nalso provide a corresponding shutdown hook to\nrelease the resources they acquired at startup.\n\nFx runs both kinds of hooks with a hard timeout enforcement.\nTherefore, hooks are expected to block only as long as they need\nto *schedule* work.\nIn other words,\n\n- hooks **must not** block to run long-running tasks synchronously\n- hooks **should** schedule long-running tasks in background goroutines\n- shutdown hooks **should** stop the background work started by startup hooks\n"
  },
  {
    "path": "docs/src/modules.md",
    "content": "---\nsidebarDepth: 2\n---\n\n# Modules\n\nAn Fx module is a shareable Go library or package\nthat provides self-contained functionality to an Fx application.\n\n## Writing modules\n\nTo write an Fx module:\n\n1. Define a top-level `Module` variable built from an `fx.Module` call.\n   Give your module a short, memorable name for logs.\n\n     ```go\n     --8<-- \"modules/module.go:start\"\n     ```\n\n2. Add components of your module with `fx.Provide`.\n\n     ```go\n     --8<--\n     modules/module.go:start\n     modules/module.go:provide\n     --8<--\n     ```\n\n3. If your module has a function that must always run,\n   add an `fx.Invoke` with it.\n\n     ```go\n     --8<--\n     modules/module.go:start\n     modules/module.go:invoke\n     --8<--\n     ```\n\n4. If your module needs to decorate its dependencies\n   before consuming them, add an `fx.Decorate` call for it.\n\n     ```go\n     --8<--\n     modules/module.go:start\n     modules/module.go:decorate\n     --8<--\n     ```\n\n5. Lastly, if you want to keep a constructor's outputs contained\n   to your module (and modules your module includes), you can\n   add an `fx.Private` when providing.\n\n     ```go\n     --8<--\n     modules/module.go:start\n     modules/module.go:privateProvide\n     --8<--\n     ```\n\n   In this case, `parseConfig` is now private to the \"server\" module.\n   No modules that contain \"server\" will be able to use the resulting\n   `Config` type because it can only be seen by the \"server\" module.\n\nThat's all there's to writing modules.\nThe rest of this section covers standards and conventions\nwe've established for writing Fx modules at Uber.\n\n### Naming\n\n#### Packages\n\nStandalone Fx modules,\ni.e. those distributed as an independent library,\nor those that have an independent Go package in a library,\nshould be named for the library they wrap\nor the functionality they provide,\nwith an added \"fx\" suffix.\n\n| Bad                | Good              |\n|--------------------|-------------------|\n| `package mylib`    | `package mylibfx` |\n| `package httputil` | `package httpfx`  |\n\nFx modules that are part of another Go package,\nor single-serving modules written for a specific application\nmay omit this suffix.\n\n#### Parameter and result objects\n\nParameter and result object types should be named\nafter the function they're for,\nby adding a `Params` or `Result` suffix to the function's name.\n\n**Exception**: If the function name begins with `New`,\nstrip the `New` prefix before adding the `Params` or `Result` suffix.\n\n| Function | Parameter object | Result object |\n|----------|------------------|---------------|\n| New      | Params           | Result        |\n| Run      | RunParams        | RunResult     |\n| NewFoo   | FooParams        | FooResult     |\n\n### Export boundary functions\n\nExport functions which are used by your module\nvia `fx.Provide` or `fx.Invoke`\nif that functionality would not be otherwise accessible.\n\n```go\n--8<-- \"modules/module.go:start\"\n--8<-- \"modules/module.go:provide\"\n--8<-- \"modules/module.go:privateProvide\"\n--8<-- \"modules/module.go:endProvide\"\n--8<-- \"modules/module.go:config\"\n--8<-- \"modules/module.go:new\"\n```\n\nIn this example, we don't export `parseConfig`,\nbecause it's a trivial `yaml.Decode` that we don't need to expose,\nbut we still export `Config` so users can decode it themselves.\n\n**Rationale**:\nIt should be possible to use your Fx module without using Fx itself.\nA user should be able to call the constructor directly\nand get the same functionality that the module would have provided with Fx.\nThis is necessary for break-glass situations and partial migrations.\n\n??? example \"Bad: No way to build the server without Fx\"\n\n    ```go\n    var Module = fx.Module(\"server\",\n      fx.Provide(newServer),\n    )\n\n    func newServer(...) (*Server, error)\n    ```\n\n### Use parameter objects\n\nFunctions exposed by a module should not\naccept dependencies directly as parameters.\nInstead, they should use a [parameter object](parameter-objects.md).\n\n```go\n--8<-- \"modules/module.go:params\"\n--8<-- \"modules/module.go:new\"\n```\n\n**Rationale**:\nModules will inevitably need to declare new dependencies.\nBy using parameter objects, we can\n[add new optional dependencies](parameter-objects.md#adding-new-parameters)\nin a backwards-compatible manner without changing the function signature.\n\n??? example \"Bad: Cannot add new parameters without breaking\"\n\n    ```go\n    func New(log *zap.Logger) (Result, error)\n    ```\n\n### Use result objects\n\nFunctions exposed by a module should not\ndeclare their results as regular return values.\nInstead, they should use a [result object](result-objects.md).\n\n```go\n--8<-- \"modules/module.go:result\"\n--8<-- \"modules/module.go:new\"\n```\n\n**Rationale**:\nModules will inevitably need to return new results.\nBy using result objects, we can\n[produce new results](result-objects.md#adding-new-results)\nin a backwards-compatible manner without changing the function signature.\n\n??? example \"Bad: Cannot add new results without breaking\"\n\n    ```go\n    func New(Params) (*Server, error)\n    ```\n\n### Don't provide what you don't own\n\nFx modules should provide only those types to the application\nthat are within their purview.\nModules should not provide values they happen to use to the application.\nNor should modules bundle other modules wholesale.\n\n**Rationale**:\nThis leaves consumers free to choose how and where your dependencies come from.\nThey can use the method you recommend (e.g., \"include zapfx.Module\"),\nor build their own variant of that dependency.\n\n??? example \"Bad: Provides a dependency\"\n\n    ```go\n    package httpfx\n\n    type Result struct {\n    \tfx.Out\n\n    \tClient *http.Client\n    \tLogger *zap.Logger // BAD\n    }\n    ```\n\n??? example \"Bad: Bundles another module\"\n\n    ```go\n    package httpfx\n\n    var Module = fx.Module(\"http\",\n    \tfx.Provide(New),\n    \tzapfx.Module, // BAD\n    )\n    ```\n\n**Exception**:\nOrganization or team-level \"kitchen sink\" modules\nthat exists solely to bundle other modules\nmay ignore this rule.\nFor example, at Uber we define an `uberfx.Module`\nthat bundles several other independent modules.\nEverything in this module is required by *all* services.\n\n### Keep independent modules thin\n\nIndependent Fx modules--those with names ending with \"fx\"\nrarely contain non-trivial business logic.\nIf an Fx module is inside a package\nthat contains significant business logic,\nit should not have the \"fx\" suffix in its name.\n\n**Rationale**:\nIt should be possible for someone to migrate to or away from Fx,\nwithout rewriting their business logic.\n\n??? example \"Good: Business logic consumes net/http.Client\"\n\n    ```go\n    package httpfx\n\n    import \"net/http\"\n\n    type Result struct {\n    \tfx.Out\n\n    \tClient *http.Client\n    }\n    ```\n\n??? example \"Bad: Fx module implements logger\"\n\n    ```go\n    package logfx\n\n    type Logger struct {\n     // ...\n    }\n\n    func New(...) Logger\n    ```\n\n### Invoke sparingly\n\nBe deliberate in your choice to use `fx.Invoke` in your module.\nBy design, Fx executes constructors added via `fx.Provide`\nonly if the application consumes its result, either directly or indirectly,\nthrough another module, constructor, or invoke.\nOn the other hand, functions added with `fx.Invoke` run unconditionally,\nand in doing so instantiate every direct and transitive value they depend on.\n"
  },
  {
    "path": "docs/src/parameter-objects.md",
    "content": "# Parameter Objects\n\nA parameter object is an object with the sole purpose of carrying parameters\nfor a specific function or method.\n\nThe object is typically defined exclusively for that function,\nand is not shared with other functions.\nThat is, a parameter object is not a general purpose object like \"user\"\nbut purpose-built, like \"parameters for the `GetUser` function\".\n\nIn Fx, parameter objects contain exported fields exclusively,\nand are always tagged with `fx.In`.\n\n**Related**\n\n- [Result objects](result-objects.md) are the result analog of\n  parameter objects.\n\n## Using parameter objects\n\nTo use parameter objects in Fx, take the following steps:\n\n1. Define a new struct type named after your constructor\n   with a `Params` suffix.\n   If the constructor is named `NewClient`, name the struct `ClientParams`.\n   If the constructor is named `New`, name the struct `Params`.\n   This naming isn't strictly necessary, but it's a good convention to follow.\n\n     ```go\n     --8<-- \"parameter-objects/define.go:empty-1\"\n     --8<-- \"parameter-objects/define.go:empty-2\"\n     ```\n\n2. Embed `fx.In` into this struct.\n\n     ```go\n     --8<-- \"parameter-objects/define.go:fxin\"\n     ```\n\n3. Add this new type as a parameter to your constructor *by value*.\n\n     ```go\n     --8<-- \"parameter-objects/define.go:takeparam\"\n     ```\n\n4. Add dependencies of your constructor as **exported** fields on this struct.\n\n     ```go\n     --8<-- \"parameter-objects/define.go:fields\"\n     ```\n\n5. Consume these fields in your constructor.\n\n     ```go\n     --8<-- \"parameter-objects/define.go:consume\"\n     ```\n\nOnce you have a parameter object on a function,\nyou can use it to access other advanced features of Fx:\n\n- [Consuming value groups with parameter objects](value-groups/consume.md#with-parameter-objects)\n\n<!--\nTODO: cover various tags supported on a parameter object.\n-->\n\n## Adding new parameters\n\nYou can add new parameters for a constructor\nby adding new fields to a parameter object.\nFor this to be backwards compatible,\nthe new fields must be **optional**.\n\n1. Take an existing parameter object.\n\n     ```go\n     --8<-- \"parameter-objects/extend.go:start-1\"\n     --8<-- \"parameter-objects/extend.go:start-2\"\n     --8<-- \"parameter-objects/extend.go:start-3\"\n     ```\n\n2. Add a new field to it for your new dependency\n   and **mark it optional** to keep this change backwards compatible.\n\n     ```go\n     --8<-- \"parameter-objects/extend.go:full\"\n     ```\n\n3. In your constructor, consume this field.\n   Be sure to handle the case when this field is absent --\n   it will take the zero value of its type in that case.\n\n     ```go\n     --8<-- \"parameter-objects/extend.go:consume\"\n     ```\n"
  },
  {
    "path": "docs/src/result-objects.md",
    "content": "# Result Objects\n\nA result object is an object with the sole purpose of carrying results\nfor a specific function or method.\n\nAs with [parameter objects](parameter-objects.md),\nthe object is defined exclusively for a single function,\nand not shared with other functions.\n\nIn Fx, result objects contain exported fields exclusively,\nand are always tagged with `fx.Out`.\n\n**Related**\n\n- [Parameter objects](parameter-objects.md) are the parameter analog of result\n  objects.\n\n## Using result objects\n\nTo use result objects in Fx, take the following steps:\n\n1. Define a new struct type named after your constructor\n   with a `Result` suffix.\n   If the constructor is named `NewClient`, name the struct `ClientResult`.\n   If the constructor is named `New`, name the struct `Result`.\n   This naming isn't strictly necessary, but it's a good convention to follow.\n\n     ```go\n     --8<-- \"result-objects/define.go:empty-1\"\n     --8<-- \"result-objects/define.go:empty-2\"\n     ```\n\n2. Embed `fx.Out` into this struct.\n\n     ```go\n     --8<-- \"result-objects/define.go:fxout\"\n     ```\n\n3. Use this new type as the return value of your constructor *by value*.\n\n     ```go\n     --8<-- \"result-objects/define.go:returnresult\"\n     ```\n\n4. Add values produced by your constructor as **exported** fields on this struct.\n\n     ```go\n     --8<-- \"result-objects/define.go:fields\"\n     ```\n\n5. Set these fields and return an instance of this struct from your\n   constructor.\n\n     ```go\n     --8<-- \"result-objects/define.go:produce\"\n     ```\n\n<!--\nTODO: cover various tags supported on a result object.\n-->\n\nOnce you have a result object on a function,\nyou can use it to access other advanced features of Fx:\n\n- [Feeding value groups with result objects](value-groups/feed.md#with-result-objects)\n\n## Adding new results\n\nYou can add new values to an existing result object\nin a completely backwards compatible manner.\n\n1. Take an existing result object.\n\n     ```go\n     --8<-- \"result-objects/extend.go:start-1\"\n     --8<-- \"result-objects/extend.go:start-2\"\n     --8<-- \"result-objects/extend.go:start-3\"\n     --8<-- \"result-objects/extend.go:start-4\"\n     ```\n\n2. Add a new field to it for your new result.\n\n     ```go\n     --8<-- \"result-objects/extend.go:full\"\n     ```\n\n3. In your constructor, set this field.\n\n     ```go\n     --8<-- \"result-objects/extend.go:produce\"\n     ```\n"
  },
  {
    "path": "docs/src/value-groups/consume.md",
    "content": "# Consuming value groups\n\nTo consume a value group of type `T`,\nyou have to tag a `[]T` dependency with `group:\"$name\"`\nwhere `$name` is the name of the value group.\n\nYou can do this in one of the following ways:\n\n- with parameter objects\n- with annotated functions\n\n## With parameter objects\n\nYou can use [parameter objects](../parameter-objects.md)\nto tag a slice parameter of a function as a value group.\n\n**Prerequisites**\n\n1. A function that consumes a parameter object.\n\n     ```go\n     --8<-- \"value-groups/consume/param.go:param-init-1\"\n     --8<-- \"value-groups/consume/param.go:param-init-2\"\n     --8<-- \"value-groups/consume/param.go:new-init\"\n     ```\n\n2. This function is provided to the Fx application.\n\n     ```go\n     --8<-- \"value-groups/consume/param.go:provide\"\n     ```\n\n**Steps**\n\n1. Add a new **exported** field to the parameter object\n   with the type `[]T`, where `T` is the kind of value in the value group.\n   Tag this field with the name of the value group.\n\n     ```go\n     --8<-- \"value-groups/consume/param.go:param-tagged\"\n     ```\n\n2. Consume this slice in the function that takes this parameter object.\n\n     ```go\n     --8<-- \"value-groups/consume/param.go:new-consume\"\n     ```\n\n!!! warning\n\n    **Do not** rely on the order of values inside the slice.\n    The order is randomized.\n\n## With annotated functions\n\nYou can use [annotations](../annotate.md)\nto consume a value group from an existing function.\n\n**Prerequisites**\n\n1. A function that accepts a slice of the kind of values in the group.\n\n     ```go\n     --8<-- \"value-groups/consume/annotate.go:new-init\"\n     ```\n\n2. The function is provided to the Fx application.\n\n     ```go\n     --8<-- \"value-groups/consume/annotate.go:provide-init\"\n     ```\n\n**Steps**\n\n1. Wrap the function passed into `fx.Provide` with `fx.Annotate`.\n\n     ```go\n     --8<-- \"value-groups/consume/annotate.go:provide-wrap-1\"\n     --8<-- \"value-groups/consume/annotate.go:provide-wrap-2\"\n     ```\n\n2. Annotate this function to state that its slice parameter is a value group.\n\n     ```go\n     --8<-- \"value-groups/consume/annotate.go:provide-annotate\"\n     ```\n\n3. Consume this slice in the function.\n\n     ```go\n     --8<-- \"value-groups/consume/annotate.go:new-consume\"\n     ```\n\n!!! tip \"Functions can accept variadic arguments\"\n\n    You can consume a value group from a function\n    that accepts variadic arguments instead of a slice.\n\n    ```go\n    --8<-- \"value-groups/consume/annotate.go:new-variadic\"\n    ```\n\n    Annotate the variadic argument like it was a slice to do this.\n\n    ```go\n    --8<-- \"value-groups/consume/annotate.go:annotate-variadic\"\n    ```\n"
  },
  {
    "path": "docs/src/value-groups/feed.md",
    "content": "# Feeding value groups\n\nTo feed values to a value group of type `T`,\nyou have to tag a `T` result with `group:\"$name\"`\nwhere `$name` is the name of the value group.\n\nYou can do this in one of the following ways:\n\n- with result objects\n- with annotated functions\n\n## With result objects\n\nYou can use [result objects](../result-objects.md)\nto tag the result of a function and feed it into a value group.\n\n**Prerequisites**\n\n1. A function that produces a result object.\n\n    ```go\n    --8<--\n    value-groups/feed/result.go:result-init-1\n    value-groups/feed/result.go:result-init-2\n    value-groups/feed/result.go:new-init-1\n    value-groups/feed/result.go:new-init-2\n    --8<--\n    ```\n\n2. The function is provided to the Fx application.\n\n    ```go\n    --8<-- \"value-groups/feed/result.go:provide\"\n    ```\n\n**Steps**\n\n1. Add a new **exported** field to the result object\n   with the type of value you want to produce,\n   and tag the field with the name of the value group.\n\n    ```go\n    --8<-- \"value-groups/feed/result.go:result-tagged\"\n    ```\n\n2. In the function, set this new field to the value\n   that you want to feed into the value group.\n\n    ```go\n    --8<-- \"value-groups/feed/result.go:new-watcher\"\n    ```\n\n## With annotated functions\n\nYou can use [annotations](../annotate.md)\nto send the result of an existing function to a value group.\n\n**Prerequisites**\n\n1. A function that produces a value of the type required by the group.\n\n    ```go\n    --8<-- \"value-groups/feed/annotate.go:new-init\"\n    ```\n\n2. The function is provided to the Fx application.\n\n    ```go\n    --8<-- \"value-groups/feed/annotate.go:provide-init\"\n    ```\n\n**Steps**\n\n1. Wrap the function passed into `fx.Provide` with `fx.Annotate`.\n\n    ```go\n    --8<-- \"value-groups/feed/annotate.go:provide-wrap-1\"\n    --8<-- \"value-groups/feed/annotate.go:provide-wrap-2\"\n    ```\n\n2. Annotate this function to state that its result feeds into the value group.\n\n    ```go\n    --8<-- \"value-groups/feed/annotate.go:provide-annotate\"\n    ```\n\n!!! tip \"Types don't always have to match\"\n\n    If the function you're annotating does not produce the same type as the group,\n    but it can be cast into that type:\n\n    ```go\n    --8<-- \"value-groups/feed/annotate.go:new-fw-init\"\n    ```\n\n    You can still use annotations to provide it to a value group.\n\n    ```go\n    --8<-- \"value-groups/feed/annotate.go:annotate-fw\"\n    ```\n\n    See [casting structs to interfaces](../annotate.md#casting-structs-to-interfaces)\n    for more details.\n"
  },
  {
    "path": "docs/src/value-groups/index.md",
    "content": "# Value Groups\n\nA *value group* is a collection of values of the same type.\nAny number of constructors across an Fx application\ncan feed values into a value group.\nSimilarly, any number of consumers can read from a value group\nwithout knowing about the full list of producers.\n\n```mermaid\nflowchart TD\n    group{{\"[]Route\"}}\n    NewA & NewB & dots[...] & NewZ --> group\n    group --> server[NewServeMux]\n    group --> NewSiteMap\n\n    style dots fill:none,stroke:none\n```\n\n!!! tip\n\n    Fx produces the values fed into a value group in a random order.\n    **Do not** make any assumptions about value group ordering.\n\n## Using value groups\n\nTo learn how to use value groups, see,\n\n- [Feeding value groups](feed.md)\n- [Consuming value groups](consume.md)\n\n## Dependency strictness\n\nDependencies formed by value groups can be:\n\n- strict: these are always consumed\n- soft: these are consumed only if the corresponding constructor\n  was requested elsewhere\n\nBy default, value group dependencies are strict.\n\n### Strict value groups\n\nStrict value group dependencies are consumed by the value group\nregardless of whether their producers are otherwise used by the application.\n\nSuppose a constructor `NewFoo` produces two values: `A` and `B`.\nValue `A` feeds into the value group `[]A`,\nwhich is then consumed by function `Run`,\nand the application invokes function `Run` with `fx.Invoke`.\n\nWith strict value groups,\nFx will run `NewFoo` to populate the `[]A` group\nregardless of whether the application consumes the other result (`B`)\ndirectly or indirectly.\n\n```mermaid\nflowchart LR\n    subgraph NewFoo\n        A; B\n    end\n    subgraph \"fx.Invoke\"\n        Run\n    end\n    A --> group{{\"[]A\"}} --> Run\n```\n\n### Soft value groups\n\nSoft value group dependencies are consumed by the value group\nonly if the constructors that produce them were called by Fx anyway --\nbecause the application consumes their other results directly or indirectly.\n\nSuppose we have a setup similar to the previous section,\nexcept that the value group is soft.\n\n```mermaid\nflowchart LR\n    subgraph NewFoo\n        A; B\n    end\n    subgraph \"fx.Invoke\"\n        Run\n    end\n    A -.-> group{{\"[]A\"}} --> Run\n```\n\nWith soft value groups,\nFx will run `NewFoo` to populate the `[]A` group\nonly if `A` or `B` are consumed by another component in the application\ndirectly or indirectly.\n\n```mermaid\nflowchart LR\n    subgraph NewFoo\n        A; B\n    end\n    subgraph \"fx.Invoke\"\n        Run; Start\n    end\n    A -.-> group{{\"[]A\"}} --> Run\n    B --> C --> Start\n```\n\n<!--\n// TODO: when to use strict vs soft value groups\n-->\n"
  },
  {
    "path": "error_example_test.go",
    "content": "// Copyright (c) 2019 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage fx_test\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"os\"\n\n\t\"go.uber.org/fx\"\n)\n\nfunc ExampleError() {\n\t// A module that provides a HTTP server depends on\n\t// the $PORT environment variable. If the variable\n\t// is unset, the module returns an fx.Error option.\n\tnewHTTPServer := func() fx.Option {\n\t\tport := os.Getenv(\"PORT\")\n\t\tif port == \"\" {\n\t\t\treturn fx.Error(errors.New(\"$PORT is not set\"))\n\t\t}\n\t\treturn fx.Provide(&http.Server{\n\t\t\tAddr: fmt.Sprintf(\"127.0.0.1:%s\", port),\n\t\t})\n\t}\n\n\tapp := fx.New(\n\t\tfx.NopLogger,\n\t\tnewHTTPServer(),\n\t\tfx.Invoke(func(s *http.Server) error { return s.ListenAndServe() }),\n\t)\n\n\tfmt.Println(app.Err())\n\n\t// Output:\n\t// $PORT is not set\n}\n"
  },
  {
    "path": "example_test.go",
    "content": "// Copyright (c) 2019 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage fx_test\n\nimport (\n\t\"context\"\n\t\"log\"\n\t\"net\"\n\t\"net/http\"\n\t\"os\"\n\t\"time\"\n\n\t\"go.uber.org/fx\"\n\t\"go.uber.org/fx/fxevent\"\n)\n\n// NewLogger constructs a logger. It's just a regular Go function, without any\n// special relationship to Fx.\n//\n// Since it returns a *log.Logger, Fx will treat NewLogger as the constructor\n// function for the standard library's logger. (We'll see how to integrate\n// NewLogger into an Fx application in the main function.) Since NewLogger\n// doesn't have any parameters, Fx will infer that loggers don't depend on any\n// other types - we can create them from thin air.\n//\n// Fx calls constructors lazily, so NewLogger will only be called only if some\n// other function needs a logger. Once instantiated, the logger is cached and\n// reused - within the application, it's effectively a singleton.\n//\n// By default, Fx applications only allow one constructor for each type. See\n// the documentation of the In and Out types for ways around this restriction.\nfunc NewLogger() *log.Logger {\n\tlogger := log.New(os.Stdout, \"\" /* prefix */, 0 /* flags */)\n\tlogger.Print(\"Executing NewLogger.\")\n\treturn logger\n}\n\n// NewHandler constructs a simple HTTP handler. Since it returns an\n// http.Handler, Fx will treat NewHandler as the constructor for the\n// http.Handler type.\n//\n// Like many Go functions, NewHandler also returns an error. If the error is\n// non-nil, Go convention tells the caller to assume that NewHandler failed\n// and the other returned values aren't safe to use. Fx understands this\n// idiom, and assumes that any function whose last return value is an error\n// follows this convention.\n//\n// Unlike NewLogger, NewHandler has formal parameters. Fx will interpret these\n// parameters as dependencies: in order to construct an HTTP handler,\n// NewHandler needs a logger. If the application has access to a *log.Logger\n// constructor (like NewLogger above), it will use that constructor or its\n// cached output and supply a logger to NewHandler. If the application doesn't\n// know how to construct a logger and needs an HTTP handler, it will fail to\n// start.\n//\n// Functions may also return multiple objects. For example, we could combine\n// NewHandler and NewLogger into a single function:\n//\n//\tfunc NewHandlerAndLogger() (*log.Logger, http.Handler, error)\n//\n// Fx also understands this idiom, and would treat NewHandlerAndLogger as the\n// constructor for both the *log.Logger and http.Handler types. Just like\n// constructors for a single type, NewHandlerAndLogger would be called at most\n// once, and both the handler and the logger would be cached and reused as\n// necessary.\nfunc NewHandler(logger *log.Logger) (http.Handler, error) {\n\tlogger.Print(\"Executing NewHandler.\")\n\treturn http.HandlerFunc(func(http.ResponseWriter, *http.Request) {\n\t\tlogger.Print(\"Got a request.\")\n\t}), nil\n}\n\n// NewMux constructs an HTTP mux. Like NewHandler, it depends on *log.Logger.\n// However, it also depends on the Fx-specific Lifecycle interface.\n//\n// A Lifecycle is available in every Fx application. It lets objects hook into\n// the application's start and stop phases. In a non-Fx application, the main\n// function often includes blocks like this:\n//\n//\tsrv, err := NewServer() // some long-running network server\n//\tif err != nil {\n//\t  log.Fatalf(\"failed to construct server: %v\", err)\n//\t}\n//\t// Construct other objects as necessary.\n//\tgo srv.Start()\n//\tdefer srv.Stop()\n//\n// In this example, the programmer explicitly constructs a bunch of objects,\n// crashing the program if any of the constructors encounter unrecoverable\n// errors. Once all the objects are constructed, we start any background\n// goroutines and defer cleanup functions.\n//\n// Fx removes the manual object construction with dependency injection. It\n// replaces the inline goroutine spawning and deferred cleanups with the\n// Lifecycle type.\n//\n// Here, NewMux makes an HTTP mux available to other functions. Since\n// constructors are called lazily, we know that NewMux won't be called unless\n// some other function wants to register a handler. This makes it easy to use\n// Fx's Lifecycle to start an HTTP server only if we have handlers registered.\nfunc NewMux(lc fx.Lifecycle, logger *log.Logger) *http.ServeMux {\n\tlogger.Print(\"Executing NewMux.\")\n\t// First, we construct the mux and server. We don't want to start the server\n\t// until all handlers are registered.\n\tmux := http.NewServeMux()\n\tserver := &http.Server{\n\t\tAddr:    \"127.0.0.1:8080\",\n\t\tHandler: mux,\n\t}\n\t// If NewMux is called, we know that another function is using the mux. In\n\t// that case, we'll use the Lifecycle type to register a Hook that starts\n\t// and stops our HTTP server.\n\t//\n\t// Hooks are executed in dependency order. At startup, NewLogger's hooks run\n\t// before NewMux's. On shutdown, the order is reversed.\n\t//\n\t// Returning an error from OnStart hooks interrupts application startup. Fx\n\t// immediately runs the OnStop portions of any successfully-executed OnStart\n\t// hooks (so that types which started cleanly can also shut down cleanly),\n\t// then exits.\n\t//\n\t// Returning an error from OnStop hooks logs a warning, but Fx continues to\n\t// run the remaining hooks.\n\tlc.Append(fx.Hook{\n\t\t// To mitigate the impact of deadlocks in application startup and\n\t\t// shutdown, Fx imposes a time limit on OnStart and OnStop hooks. By\n\t\t// default, hooks have a total of 15 seconds to complete. Timeouts are\n\t\t// passed via Go's usual context.Context.\n\t\tOnStart: func(context.Context) error {\n\t\t\tlogger.Print(\"Starting HTTP server.\")\n\t\t\tln, err := net.Listen(\"tcp\", server.Addr)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tgo server.Serve(ln)\n\t\t\treturn nil\n\t\t},\n\t\tOnStop: func(ctx context.Context) error {\n\t\t\tlogger.Print(\"Stopping HTTP server.\")\n\t\t\treturn server.Shutdown(ctx)\n\t\t},\n\t})\n\n\treturn mux\n}\n\n// Register mounts our HTTP handler on the mux.\n//\n// Register is a typical top-level application function: it takes a generic\n// type like ServeMux, which typically comes from a third-party library, and\n// introduces it to a type that contains our application logic. In this case,\n// that introduction consists of registering an HTTP handler. Other typical\n// examples include registering RPC procedures and starting queue consumers.\n//\n// Fx calls these functions invocations, and they're treated differently from\n// the constructor functions above. Their arguments are still supplied via\n// dependency injection and they may still return an error to indicate\n// failure, but any other return values are ignored.\n//\n// Unlike constructors, invocations are called eagerly. See the main function\n// below for details.\nfunc Register(mux *http.ServeMux, h http.Handler) {\n\tmux.Handle(\"/\", h)\n}\n\nfunc Example() {\n\tapp := fx.New(\n\t\t// Provide all the constructors we need, which teaches Fx how we'd like to\n\t\t// construct the *log.Logger, http.Handler, and *http.ServeMux types.\n\t\t// Remember that constructors are called lazily, so this block doesn't do\n\t\t// much on its own.\n\t\tfx.Provide(\n\t\t\tNewLogger,\n\t\t\tNewHandler,\n\t\t\tNewMux,\n\t\t),\n\t\t// Since constructors are called lazily, we need some invocations to\n\t\t// kick-start our application. In this case, we'll use Register. Since it\n\t\t// depends on an http.Handler and *http.ServeMux, calling it requires Fx\n\t\t// to build those types using the constructors above. Since we call\n\t\t// NewMux, we also register Lifecycle hooks to start and stop an HTTP\n\t\t// server.\n\t\tfx.Invoke(Register),\n\n\t\t// This is optional. With this, you can control where Fx logs\n\t\t// its events. In this case, we're using a NopLogger to keep\n\t\t// our test silent. Normally, you'll want to use an\n\t\t// fxevent.ZapLogger or an fxevent.ConsoleLogger.\n\t\tfx.WithLogger(\n\t\t\tfunc() fxevent.Logger {\n\t\t\t\treturn fxevent.NopLogger\n\t\t\t},\n\t\t),\n\t)\n\n\t// In a typical application, we could just use app.Run() here. Since we\n\t// don't want this example to run forever, we'll use the more-explicit Start\n\t// and Stop.\n\tstartCtx, cancel := context.WithTimeout(context.Background(), 15*time.Second)\n\tdefer cancel()\n\tif err := app.Start(startCtx); err != nil {\n\t\tlog.Fatal(err)\n\t}\n\n\t// Normally, we'd block here with <-app.Done(). Instead, we'll make an HTTP\n\t// request to demonstrate that our server is running.\n\tif _, err := http.Get(\"http://localhost:8080/\"); err != nil {\n\t\tlog.Fatal(err)\n\t}\n\n\tstopCtx, cancel := context.WithTimeout(context.Background(), 15*time.Second)\n\tdefer cancel()\n\tif err := app.Stop(stopCtx); err != nil {\n\t\tlog.Fatal(err)\n\t}\n\n\t// Output:\n\t// Executing NewLogger.\n\t// Executing NewMux.\n\t// Executing NewHandler.\n\t// Starting HTTP server.\n\t// Got a request.\n\t// Stopping HTTP server.\n}\n"
  },
  {
    "path": "extract.go",
    "content": "// Copyright (c) 2019 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage fx\n\nimport (\n\t\"fmt\"\n\t\"reflect\"\n\t\"unicode\"\n\t\"unicode/utf8\"\n)\n\nvar _typeOfIn = reflect.TypeOf(In{})\n\n// Extract fills the given struct with values from the dependency injection\n// container on application initialization. The target MUST be a pointer to a\n// struct. Only exported fields will be filled.\n//\n// Deprecated: Use Populate instead.\nfunc Extract(target any) Option {\n\tv := reflect.ValueOf(target)\n\n\tif t := v.Type(); t.Kind() != reflect.Ptr || t.Elem().Kind() != reflect.Struct {\n\t\treturn Error(fmt.Errorf(\"Extract expected a pointer to a struct, got a %v\", t))\n\t}\n\n\tv = v.Elem()\n\tt := v.Type()\n\n\t// We generate a function which accepts a single fx.In struct as an\n\t// argument. This struct contains all exported fields of the target\n\t// struct.\n\n\t// Fields of the generated fx.In struct.\n\tfields := make([]reflect.StructField, 0, t.NumField()+1)\n\n\t// Anonymous dig.In field.\n\tfields = append(fields, reflect.StructField{\n\t\tName:      _typeOfIn.Name(),\n\t\tAnonymous: true,\n\t\tType:      _typeOfIn,\n\t})\n\n\t// List of values in the target struct aligned with the fields of the\n\t// generated struct.\n\t//\n\t// So for example, if the target is,\n\t//\n\t// \tvar target struct {\n\t// \t\tFoo io.Reader\n\t// \t\tbar []byte\n\t// \t\tBaz io.Writer\n\t// \t}\n\t//\n\t// The generated struct has the shape,\n\t//\n\t// \tstruct {\n\t// \t\tfx.In\n\t//\n\t// \t\tF0 io.Reader\n\t// \t\tF2 io.Writer\n\t// \t}\n\t//\n\t// And `targets` is,\n\t//\n\t// \t[\n\t// \t\ttarget.Field(0),  // Foo io.Reader\n\t// \t\ttarget.Field(2),  // Baz io.Writer\n\t// \t]\n\t//\n\t// As we iterate through the fields of the generated struct, we can copy\n\t// the value into the corresponding value in the targets list.\n\ttargets := make([]reflect.Value, 0, t.NumField())\n\n\tfor i := 0; i < t.NumField(); i++ {\n\t\tf := t.Field(i)\n\n\t\t// Skip unexported fields.\n\t\tif f.Anonymous {\n\t\t\t// If embedded, StructField.PkgPath is not a reliable indicator of\n\t\t\t// whether the field is exported. See\n\t\t\t// https://github.com/golang/go/issues/21122\n\n\t\t\tt := f.Type\n\t\t\tif t.Kind() == reflect.Ptr {\n\t\t\t\tt = t.Elem()\n\t\t\t}\n\n\t\t\tif !isExported(t.Name()) {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t} else if f.PkgPath != \"\" {\n\t\t\tcontinue\n\t\t}\n\n\t\t// We don't copy over names or embedded semantics.\n\t\tfields = append(fields, reflect.StructField{\n\t\t\tName: fmt.Sprintf(\"F%d\", i),\n\t\t\tType: f.Type,\n\t\t\tTag:  f.Tag,\n\t\t})\n\t\ttargets = append(targets, v.Field(i))\n\t}\n\n\t// Equivalent to,\n\t//\n\t// \tfunc(r struct {\n\t// \t\tfx.In\n\t//\n\t// \t\tF1 Foo\n\t// \t\tF2 Bar\n\t// \t}) {\n\t// \t\ttarget.Foo = r.F1\n\t// \t\ttarget.Bar = r.F2\n\t// \t}\n\n\tfn := reflect.MakeFunc(\n\t\treflect.FuncOf(\n\t\t\t[]reflect.Type{reflect.StructOf(fields)},\n\t\t\tnil,   /* results */\n\t\t\tfalse, /* variadic */\n\t\t),\n\t\tfunc(args []reflect.Value) []reflect.Value {\n\t\t\tresult := args[0]\n\t\t\tfor i := 1; i < result.NumField(); i++ {\n\t\t\t\ttargets[i-1].Set(result.Field(i))\n\t\t\t}\n\t\t\treturn nil\n\t\t},\n\t)\n\n\treturn Invoke(fn.Interface())\n}\n\n// isExported reports whether the identifier is exported.\nfunc isExported(id string) bool {\n\tr, _ := utf8.DecodeRuneInString(id)\n\treturn unicode.IsUpper(r)\n}\n"
  },
  {
    "path": "extract_test.go",
    "content": "// Copyright (c) 2019 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage fx_test\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"testing\"\n\n\t. \"go.uber.org/fx\"\n\t\"go.uber.org/fx/fxtest\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\t\"go.uber.org/dig\"\n)\n\nfunc TestExtract(t *testing.T) {\n\tt.Parallel()\n\n\ttype type1 struct{}\n\ttype type2 struct{}\n\ttype type3 struct{}\n\n\tt.Run(\"Failures\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\ttests := []any{\n\t\t\t3,\n\t\t\tfunc() {},\n\t\t\tstruct{}{},\n\t\t\tstruct{ Foo *bytes.Buffer }{},\n\t\t}\n\n\t\tfor _, tt := range tests {\n\t\t\tt.Run(fmt.Sprintf(\"%T\", tt), func(t *testing.T) {\n\t\t\t\tt.Parallel()\n\n\t\t\t\tapp := NewForTest(t,\n\t\t\t\t\tProvide(func() *bytes.Buffer { return &bytes.Buffer{} }),\n\t\t\t\t\tExtract(&tt),\n\t\t\t\t)\n\t\t\t\terr := app.Err()\n\t\t\t\trequire.Error(t, err)\n\t\t\t\tassert.Contains(t, err.Error(), \"Extract expected a pointer to a struct\")\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"ValidateApp\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\ttests := []any{\n\t\t\t3,\n\t\t\tfunc() {},\n\t\t\tstruct{}{},\n\t\t\tstruct{ Foo *bytes.Buffer }{},\n\t\t}\n\n\t\tfor _, tt := range tests {\n\t\t\tt.Run(fmt.Sprintf(\"%T\", tt), func(t *testing.T) {\n\t\t\t\tt.Parallel()\n\n\t\t\t\terr := validateTestApp(t,\n\t\t\t\t\tProvide(func() *bytes.Buffer { return &bytes.Buffer{} }),\n\t\t\t\t\tExtract(&tt),\n\t\t\t\t)\n\n\t\t\t\trequire.Error(t, err)\n\t\t\t\tassert.Contains(t, err.Error(), \"Extract expected a pointer to a struct\")\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"Empty\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tnew1 := func() *type1 { panic(\"new1 must not be called\") }\n\t\tnew2 := func() *type2 { panic(\"new2 must not be called\") }\n\n\t\tvar out struct{}\n\t\tapp := fxtest.New(t,\n\t\t\tProvide(new1, new2),\n\t\t\tExtract(&out),\n\t\t)\n\t\tapp.RequireStart().RequireStop()\n\t})\n\n\tt.Run(\"StructIsExtracted\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tvar gave1 *type1\n\t\tnew1 := func() *type1 {\n\t\t\tgave1 = &type1{}\n\t\t\treturn gave1\n\t\t}\n\n\t\tvar gave2 *type2\n\t\tnew2 := func() *type2 {\n\t\t\tgave2 = &type2{}\n\t\t\treturn gave2\n\t\t}\n\n\t\tvar out struct {\n\t\t\tT1 *type1\n\t\t\tT2 *type2\n\t\t}\n\n\t\tapp := fxtest.New(t,\n\t\t\tProvide(new1, new2),\n\t\t\tExtract(&out),\n\t\t)\n\n\t\tdefer app.RequireStart().RequireStop()\n\t\tassert.NotNil(t, out.T1, \"T1 must not be nil\")\n\t\tassert.NotNil(t, out.T2, \"T2 must not be nil\")\n\t\tassert.True(t, gave1 == out.T1, \"T1 must match\")\n\t\tassert.True(t, gave2 == out.T2, \"T2 must match\")\n\t})\n\n\tt.Run(\"EmbeddedExportedField\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\ttype T1 struct{}\n\n\t\tvar gave1 *T1\n\t\tnew1 := func() *T1 {\n\t\t\tgave1 = &T1{}\n\t\t\treturn gave1\n\t\t}\n\n\t\tvar out struct{ *T1 }\n\n\t\tapp := fxtest.New(t,\n\t\t\tProvide(new1),\n\t\t\tExtract(&out),\n\t\t)\n\n\t\tdefer app.RequireStart().RequireStop()\n\t\tassert.NotNil(t, out.T1, \"T1 must not be nil\")\n\t\tassert.True(t, gave1 == out.T1, \"T1 must match\")\n\t})\n\n\tt.Run(\"EmbeddedUnexportedField\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tnew1 := func() *type1 { return &type1{} }\n\t\tvar out struct{ *type1 }\n\n\t\tapp := fxtest.New(t, Provide(new1), Extract(&out))\n\t\tdefer app.RequireStart().RequireStop()\n\n\t\t// Unexported fields are left unchanged.\n\t\tassert.Nil(t, out.type1, \"type1 must be nil\")\n\t})\n\n\tt.Run(\"EmbeddedUnexportedFieldValue\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\ttype type4 struct{ foo string }\n\t\tnew4 := func() type4 { return type4{\"foo\"} }\n\t\tvar out struct{ type4 }\n\n\t\tapp := fxtest.New(t, Provide(new4), Extract(&out))\n\t\tdefer app.RequireStart().RequireStop()\n\n\t\t// Unexported fields are left unchanged.\n\t\tassert.NotEqual(t, \"foo\", out.foo)\n\t})\n\n\tt.Run(\"DuplicateFields\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tvar gave *type1\n\t\tnew1 := func() *type1 {\n\t\t\trequire.Nil(t, gave, \"gave must be nil\")\n\t\t\tgave = &type1{}\n\t\t\treturn gave\n\t\t}\n\n\t\tvar out struct {\n\t\t\tX *type1\n\t\t\tY *type1\n\t\t}\n\n\t\tapp := fxtest.New(t,\n\t\t\tProvide(new1),\n\t\t\tExtract(&out),\n\t\t)\n\n\t\tdefer app.RequireStart().RequireStop()\n\t\tassert.NotNil(t, out.X, \"X must not be nil\")\n\t\tassert.NotNil(t, out.Y, \"Y must not be nil\")\n\t\tassert.True(t, gave == out.X, \"X must match\")\n\t\tassert.True(t, gave == out.Y, \"Y must match\")\n\t})\n\n\tt.Run(\"SkipsUnexported\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tvar gave1 *type1\n\t\tnew1 := func() *type1 {\n\t\t\tgave1 = &type1{}\n\t\t\treturn gave1\n\t\t}\n\n\t\tnew2 := func() *type2 { panic(\"new2 must not be called\") }\n\n\t\tvar gave3 *type3\n\t\tnew3 := func() *type3 {\n\t\t\tgave3 = &type3{}\n\t\t\treturn gave3\n\t\t}\n\n\t\tvar out struct {\n\t\t\tT1 *type1\n\t\t\tt2 *type2\n\t\t\tT3 *type3\n\t\t}\n\n\t\tapp := fxtest.New(t,\n\t\t\tProvide(new1, new2, new3),\n\t\t\tExtract(&out),\n\t\t)\n\n\t\tdefer app.RequireStart().RequireStop()\n\t\tassert.NotNil(t, out.T1, \"T1 must not be nil\")\n\t\tassert.Nil(t, out.t2, \"t2 must be nil\")\n\t\tassert.NotNil(t, out.T3, \"T3 must not be nil\")\n\t\tassert.True(t, gave1 == out.T1, \"T1 must match\")\n\t\tassert.True(t, gave3 == out.T3, \"T3 must match\")\n\t})\n\n\tt.Run(\"DoesNotZeroUnexported\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tvar gave1 *type1\n\t\tnew1 := func() *type1 {\n\t\t\tgave1 = &type1{}\n\t\t\treturn gave1\n\t\t}\n\n\t\tnew2 := func() *type2 { panic(\"new2 must not be called\") }\n\n\t\tvar out struct {\n\t\t\tT1 *type1\n\t\t\tt2 *type2\n\t\t}\n\t\tt2 := &type2{}\n\t\tout.t2 = t2\n\n\t\tapp := fxtest.New(t,\n\t\t\tProvide(new1, new2),\n\t\t\tExtract(&out),\n\t\t)\n\n\t\tdefer app.RequireStart().RequireStop()\n\t\tassert.NotNil(t, out.T1, \"T1 must not be nil\")\n\t\tassert.NotNil(t, out.t2, \"t2 must not be nil\")\n\t\tassert.True(t, gave1 == out.T1, \"T1 must match\")\n\t\tassert.True(t, t2 == out.t2, \"t2 must match\")\n\t})\n\n\tt.Run(\"TopLevelDigIn\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tvar out struct{ dig.In }\n\t\tapp := fxtest.New(t, Extract(&out))\n\t\tdefer app.RequireStart().RequireStop()\n\t})\n\n\tt.Run(\"TopLevelFxIn\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tnew1 := func() *type1 { panic(\"new1 must not be called\") }\n\t\tnew2 := func() *type2 { panic(\"new2 must not be called\") }\n\n\t\tvar out struct{ In }\n\t\tapp := fxtest.New(t,\n\t\t\tProvide(new1, new2),\n\t\t\tExtract(&out),\n\t\t)\n\n\t\tdefer app.RequireStart().RequireStop()\n\t})\n\n\tt.Run(\"NestedFxIn\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tvar gave1 *type1\n\t\tnew1 := func() *type1 {\n\t\t\tgave1 = &type1{}\n\t\t\treturn gave1\n\t\t}\n\n\t\tvar out struct {\n\t\t\tResult struct {\n\t\t\t\tIn\n\n\t\t\t\tT1 *type1\n\t\t\t\tT2 *type2 `optional:\"true\"`\n\t\t\t}\n\t\t}\n\n\t\tapp := fxtest.New(t,\n\t\t\tProvide(new1),\n\t\t\tExtract(&out),\n\t\t)\n\n\t\tdefer app.RequireStart().RequireStop()\n\t\tassert.NotNil(t, out.Result.T1, \"T1 must not be nil\")\n\t\tassert.Nil(t, out.Result.T2, \"T2 must be nil\")\n\t\tassert.True(t, gave1 == out.Result.T1, \"T1 must match\")\n\t})\n\n\tt.Run(\"FurtherNestedFxIn\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tvar out struct {\n\t\t\tIn\n\n\t\t\tB struct {\n\t\t\t\tIn\n\n\t\t\t\tC int\n\t\t\t}\n\t\t}\n\n\t\tapp := fxtest.New(t,\n\t\t\tProvide(func() int { return 42 }),\n\t\t\tExtract(&out),\n\t\t)\n\t\tdefer app.RequireStart().RequireStop()\n\t\tassert.Equal(t, 42, out.B.C, \"B.C must match\")\n\t})\n\n\tt.Run(\"FieldsCanBeOptional\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tvar gave1 *type1\n\t\tnew1 := func() *type1 {\n\t\t\tgave1 = &type1{}\n\t\t\treturn gave1\n\t\t}\n\n\t\tvar out struct {\n\t\t\tT1 *type1\n\t\t\tT2 *type2 `optional:\"true\"`\n\t\t}\n\n\t\tapp := fxtest.New(t,\n\t\t\tProvide(new1),\n\t\t\tExtract(&out),\n\t\t)\n\n\t\tdefer app.RequireStart().RequireStop()\n\t\tassert.NotNil(t, out.T1, \"T1 must not be nil\")\n\t\tassert.Nil(t, out.T2, \"T2 must be nil\")\n\n\t\tassert.True(t, gave1 == out.T1, \"T1 must match\")\n\t})\n}\n"
  },
  {
    "path": "fxevent/console.go",
    "content": "// Copyright (c) 2021 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage fxevent\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"strings\"\n)\n\n// ConsoleLogger is an Fx event logger that attempts to write human-readable\n// messages to the console.\n//\n// Use this during development.\ntype ConsoleLogger struct {\n\tW io.Writer\n}\n\nvar _ Logger = (*ConsoleLogger)(nil)\n\nfunc (l *ConsoleLogger) logf(msg string, args ...any) {\n\tfmt.Fprintf(l.W, \"[Fx] \"+msg+\"\\n\", args...)\n}\n\n// LogEvent logs the given event to the provided Zap logger.\nfunc (l *ConsoleLogger) LogEvent(event Event) {\n\tswitch e := event.(type) {\n\tcase *OnStartExecuting:\n\t\tl.logf(\"HOOK OnStart\\t\\t%s executing (caller: %s)\", e.FunctionName, e.CallerName)\n\tcase *OnStartExecuted:\n\t\tif e.Err != nil {\n\t\t\tl.logf(\"HOOK OnStart\\t\\t%s called by %s failed in %s: %+v\", e.FunctionName, e.CallerName, e.Runtime, e.Err)\n\t\t} else {\n\t\t\tl.logf(\"HOOK OnStart\\t\\t%s called by %s ran successfully in %s\", e.FunctionName, e.CallerName, e.Runtime)\n\t\t}\n\tcase *OnStopExecuting:\n\t\tl.logf(\"HOOK OnStop\\t\\t%s executing (caller: %s)\", e.FunctionName, e.CallerName)\n\tcase *OnStopExecuted:\n\t\tif e.Err != nil {\n\t\t\tl.logf(\"HOOK OnStop\\t\\t%s called by %s failed in %s: %+v\", e.FunctionName, e.CallerName, e.Runtime, e.Err)\n\t\t} else {\n\t\t\tl.logf(\"HOOK OnStop\\t\\t%s called by %s ran successfully in %s\", e.FunctionName, e.CallerName, e.Runtime)\n\t\t}\n\tcase *Supplied:\n\t\tif e.Err != nil {\n\t\t\tl.logf(\"ERROR\\tFailed to supply %v: %+v\", e.TypeName, e.Err)\n\t\t} else if e.ModuleName != \"\" {\n\t\t\tl.logf(\"SUPPLY\\t%v from module %q\", e.TypeName, e.ModuleName)\n\t\t} else {\n\t\t\tl.logf(\"SUPPLY\\t%v\", e.TypeName)\n\t\t}\n\tcase *Provided:\n\t\tvar privateStr string\n\t\tif e.Private {\n\t\t\tprivateStr = \" (PRIVATE)\"\n\t\t}\n\t\tfor _, rtype := range e.OutputTypeNames {\n\t\t\tif e.ModuleName != \"\" {\n\t\t\t\tl.logf(\"PROVIDE%v\\t%v <= %v from module %q\", privateStr, rtype, e.ConstructorName, e.ModuleName)\n\t\t\t} else {\n\t\t\t\tl.logf(\"PROVIDE%v\\t%v <= %v\", privateStr, rtype, e.ConstructorName)\n\t\t\t}\n\t\t}\n\t\tif e.Err != nil {\n\t\t\tl.logf(\"Error after options were applied: %+v\", e.Err)\n\t\t}\n\tcase *Replaced:\n\t\tfor _, rtype := range e.OutputTypeNames {\n\t\t\tif e.ModuleName != \"\" {\n\t\t\t\tl.logf(\"REPLACE\\t%v from module %q\", rtype, e.ModuleName)\n\t\t\t} else {\n\t\t\t\tl.logf(\"REPLACE\\t%v\", rtype)\n\t\t\t}\n\t\t}\n\t\tif e.Err != nil {\n\t\t\tl.logf(\"ERROR\\tFailed to replace: %+v\", e.Err)\n\t\t}\n\tcase *Decorated:\n\t\tfor _, rtype := range e.OutputTypeNames {\n\t\t\tif e.ModuleName != \"\" {\n\t\t\t\tl.logf(\"DECORATE\\t%v <= %v from module %q\", rtype, e.DecoratorName, e.ModuleName)\n\t\t\t} else {\n\t\t\t\tl.logf(\"DECORATE\\t%v <= %v\", rtype, e.DecoratorName)\n\t\t\t}\n\t\t}\n\t\tif e.Err != nil {\n\t\t\tl.logf(\"Error after options were applied: %+v\", e.Err)\n\t\t}\n\tcase *BeforeRun:\n\t\tvar moduleStr string\n\t\tif e.ModuleName != \"\" {\n\t\t\tmoduleStr = fmt.Sprintf(\" from module %q\", e.ModuleName)\n\t\t}\n\t\tl.logf(\"BEFORE RUN\\t%s: %s%s\", e.Kind, e.Name, moduleStr)\n\tcase *Run:\n\t\tvar moduleStr string\n\t\tif e.ModuleName != \"\" {\n\t\t\tmoduleStr = fmt.Sprintf(\" from module %q\", e.ModuleName)\n\t\t}\n\t\tl.logf(\"RUN\\t%v: %v in %s%v\", e.Kind, e.Name, e.Runtime, moduleStr)\n\t\tif e.Err != nil {\n\t\t\tl.logf(\"Error returned: %+v\", e.Err)\n\t\t}\n\n\tcase *Invoking:\n\t\tif e.ModuleName != \"\" {\n\t\t\tl.logf(\"INVOKE\\t\\t%s from module %q\", e.FunctionName, e.ModuleName)\n\t\t} else {\n\t\t\tl.logf(\"INVOKE\\t\\t%s\", e.FunctionName)\n\t\t}\n\tcase *Invoked:\n\t\tif e.Err != nil {\n\t\t\tl.logf(\"ERROR\\t\\tfx.Invoke(%v) called from:\\n%+vFailed: %+v\", e.FunctionName, e.Trace, e.Err)\n\t\t}\n\tcase *Stopping:\n\t\tl.logf(\"%v\", strings.ToUpper(e.Signal.String()))\n\tcase *Stopped:\n\t\tif e.Err != nil {\n\t\t\tl.logf(\"ERROR\\t\\tFailed to stop cleanly: %+v\", e.Err)\n\t\t}\n\tcase *RollingBack:\n\t\tl.logf(\"ERROR\\t\\tStart failed, rolling back: %+v\", e.StartErr)\n\tcase *RolledBack:\n\t\tif e.Err != nil {\n\t\t\tl.logf(\"ERROR\\t\\tCouldn't roll back cleanly: %+v\", e.Err)\n\t\t}\n\tcase *Started:\n\t\tif e.Err != nil {\n\t\t\tl.logf(\"ERROR\\t\\tFailed to start: %+v\", e.Err)\n\t\t} else {\n\t\t\tl.logf(\"RUNNING\")\n\t\t}\n\tcase *LoggerInitialized:\n\t\tif e.Err != nil {\n\t\t\tl.logf(\"ERROR\\t\\tFailed to initialize custom logger: %+v\", e.Err)\n\t\t} else {\n\t\t\tl.logf(\"LOGGER\\tInitialized custom logger from %v\", e.ConstructorName)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "fxevent/console_test.go",
    "content": "// Copyright (c) 2021 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage fxevent\n\nimport (\n\t\"bytes\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"os\"\n\t\"strings\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\n// richError prints a different output when formatted with %+v vs %v.\ntype richError struct{}\n\nfunc (e *richError) Error() string { return \"plain error\" }\n\nfunc (e *richError) Format(w fmt.State, c rune) {\n\tif w.Flag('+') && c == 'v' {\n\t\t// Format differently for %+v.\n\t\tio.WriteString(w, \"rich error\")\n\t} else {\n\t\tio.WriteString(w, e.Error())\n\t}\n}\n\nfunc TestConsoleLogger(t *testing.T) {\n\tt.Parallel()\n\n\ttests := []struct {\n\t\tname string\n\t\tgive Event\n\t\twant string\n\t}{\n\t\t{\n\t\t\tname: \"OnStart executing\",\n\t\t\tgive: &OnStartExecuting{\n\t\t\t\tFunctionName: \"hook.onStart\",\n\t\t\t\tCallerName:   \"bytes.NewBuffer\",\n\t\t\t},\n\t\t\twant: \"[Fx] HOOK OnStart\t\thook.onStart executing (caller: bytes.NewBuffer)\\n\",\n\t\t},\n\t\t{\n\t\t\tname: \"OnStopExecuting\",\n\t\t\tgive: &OnStopExecuting{\n\t\t\t\tFunctionName: \"hook.onStop1\",\n\t\t\t\tCallerName:   \"bytes.NewBuffer\",\n\t\t\t},\n\t\t\twant: \"[Fx] HOOK OnStop\t\thook.onStop1 executing (caller: bytes.NewBuffer)\\n\",\n\t\t},\n\t\t{\n\t\t\tname: \"OnStopExecutedError\",\n\t\t\tgive: &OnStopExecuted{\n\t\t\t\tFunctionName: \"hook.onStart1\",\n\t\t\t\tCallerName:   \"bytes.NewBuffer\",\n\t\t\t\tErr:          fmt.Errorf(\"some error\"),\n\t\t\t},\n\t\t\twant: \"[Fx] HOOK OnStop\t\thook.onStart1 called by bytes.NewBuffer failed in 0s: some error\\n\",\n\t\t},\n\t\t{\n\t\t\tname: \"OnStopExecutedError/rich error\",\n\t\t\tgive: &OnStopExecuted{\n\t\t\t\tFunctionName: \"hook.onStart1\",\n\t\t\t\tCallerName:   \"bytes.NewBuffer\",\n\t\t\t\tErr:          &richError{},\n\t\t\t},\n\t\t\twant: \"[Fx] HOOK OnStop\t\thook.onStart1 called by bytes.NewBuffer failed in 0s: rich error\\n\",\n\t\t},\n\t\t{\n\t\t\tname: \"OnStopExecuted\",\n\t\t\tgive: &OnStopExecuted{\n\t\t\t\tFunctionName: \"hook.onStart1\",\n\t\t\t\tCallerName:   \"bytes.NewBuffer\",\n\t\t\t\tRuntime:      time.Millisecond * 3,\n\t\t\t},\n\t\t\twant: \"[Fx] HOOK OnStop\t\thook.onStart1 called by bytes.NewBuffer ran successfully in 3ms\\n\",\n\t\t},\n\t\t{\n\t\t\tname: \"OnStartExecutedError\",\n\t\t\tgive: &OnStartExecuted{\n\t\t\t\tFunctionName: \"hook.onStart1\",\n\t\t\t\tCallerName:   \"bytes.NewBuffer\",\n\t\t\t\tErr:          fmt.Errorf(\"some error\"),\n\t\t\t},\n\t\t\twant: \"[Fx] HOOK OnStart\t\thook.onStart1 called by bytes.NewBuffer failed in 0s: some error\\n\",\n\t\t},\n\t\t{\n\t\t\tname: \"OnStartExecutedError/rich error\",\n\t\t\tgive: &OnStartExecuted{\n\t\t\t\tFunctionName: \"hook.onStart1\",\n\t\t\t\tCallerName:   \"bytes.NewBuffer\",\n\t\t\t\tErr:          &richError{},\n\t\t\t},\n\t\t\twant: \"[Fx] HOOK OnStart\t\thook.onStart1 called by bytes.NewBuffer failed in 0s: rich error\\n\",\n\t\t},\n\t\t{\n\t\t\tname: \"OnStartExecuted\",\n\t\t\tgive: &OnStartExecuted{\n\t\t\t\tFunctionName: \"hook.onStart1\",\n\t\t\t\tCallerName:   \"bytes.NewBuffer\",\n\t\t\t\tRuntime:      time.Millisecond * 3,\n\t\t\t},\n\t\t\twant: \"[Fx] HOOK OnStart\t\thook.onStart1 called by bytes.NewBuffer ran successfully in 3ms\\n\",\n\t\t},\n\t\t{\n\t\t\tname: \"ProvideError\",\n\t\t\tgive: &Provided{Err: errors.New(\"some error\")},\n\t\t\twant: \"[Fx] Error after options were applied: some error\\n\",\n\t\t},\n\t\t{\n\t\t\tname: \"ProvideError/rich error\",\n\t\t\tgive: &Provided{Err: &richError{}},\n\t\t\twant: \"[Fx] Error after options were applied: rich error\\n\",\n\t\t},\n\t\t{\n\t\t\tname: \"Supplied\",\n\t\t\tgive: &Supplied{\n\t\t\t\tTypeName:    \"*bytes.Buffer\",\n\t\t\t\tStackTrace:  []string{\"main.main\", \"runtime.main\"},\n\t\t\t\tModuleTrace: []string{\"main.main\"},\n\t\t\t},\n\t\t\twant: \"[Fx] SUPPLY\t*bytes.Buffer\\n\",\n\t\t},\n\t\t{\n\t\t\tname: \"Supplied with module\",\n\t\t\tgive: &Supplied{\n\t\t\t\tTypeName:    \"*bytes.Buffer\",\n\t\t\t\tModuleName:  \"myModule\",\n\t\t\t\tStackTrace:  []string{\"main.main\", \"runtime.main\"},\n\t\t\t\tModuleTrace: []string{\"main.main\", \"mypackage.GetMyModule\"},\n\t\t\t},\n\t\t\twant: \"[Fx] SUPPLY\t*bytes.Buffer from module \\\"myModule\\\"\\n\",\n\t\t},\n\t\t{\n\t\t\tname: \"SuppliedError\",\n\t\t\tgive: &Supplied{TypeName: \"*bytes.Buffer\", Err: errors.New(\"great sadness\")},\n\t\t\twant: \"[Fx] ERROR\tFailed to supply *bytes.Buffer: great sadness\\n\",\n\t\t},\n\t\t{\n\t\t\tname: \"SuppliedError/rich error\",\n\t\t\tgive: &Supplied{TypeName: \"*bytes.Buffer\", Err: &richError{}},\n\t\t\twant: \"[Fx] ERROR\tFailed to supply *bytes.Buffer: rich error\\n\",\n\t\t},\n\t\t{\n\t\t\tname: \"Provided\",\n\t\t\tgive: &Provided{\n\t\t\t\tConstructorName: \"bytes.NewBuffer()\",\n\t\t\t\tOutputTypeNames: []string{\"*bytes.Buffer\"},\n\t\t\t\tPrivate:         false,\n\t\t\t\tStackTrace:      []string{\"main.main\", \"runtime.main\"},\n\t\t\t\tModuleTrace:     []string{\"main.main\"},\n\t\t\t},\n\t\t\twant: \"[Fx] PROVIDE\t*bytes.Buffer <= bytes.NewBuffer()\\n\",\n\t\t},\n\t\t{\n\t\t\tname: \"Provided privately\",\n\t\t\tgive: &Provided{\n\t\t\t\tConstructorName: \"bytes.NewBuffer()\",\n\t\t\t\tOutputTypeNames: []string{\"*bytes.Buffer\"},\n\t\t\t\tPrivate:         true,\n\t\t\t\tStackTrace:      []string{\"main.main\", \"runtime.main\"},\n\t\t\t\tModuleTrace:     []string{\"main.main\"},\n\t\t\t},\n\t\t\twant: \"[Fx] PROVIDE (PRIVATE)\t*bytes.Buffer <= bytes.NewBuffer()\\n\",\n\t\t},\n\t\t{\n\t\t\tname: \"Provided with module\",\n\t\t\tgive: &Provided{\n\t\t\t\tConstructorName: \"bytes.NewBuffer()\",\n\t\t\t\tModuleName:      \"myModule\",\n\t\t\t\tOutputTypeNames: []string{\"*bytes.Buffer\"},\n\t\t\t\tPrivate:         false,\n\t\t\t\tStackTrace:      []string{\"main.main\", \"runtime.main\"},\n\t\t\t\tModuleTrace:     []string{\"main.main\", \"mypackage.GetMyModule\"},\n\t\t\t},\n\t\t\twant: \"[Fx] PROVIDE\t*bytes.Buffer <= bytes.NewBuffer() from module \\\"myModule\\\"\\n\",\n\t\t},\n\t\t{\n\t\t\tname: \"Provided with module privately\",\n\t\t\tgive: &Provided{\n\t\t\t\tConstructorName: \"bytes.NewBuffer()\",\n\t\t\t\tModuleName:      \"myModule\",\n\t\t\t\tOutputTypeNames: []string{\"*bytes.Buffer\"},\n\t\t\t\tPrivate:         true,\n\t\t\t\tStackTrace:      []string{\"main.main\", \"runtime.main\"},\n\t\t\t\tModuleTrace:     []string{\"main.main\", \"mypackage.GetMyModule\"},\n\t\t\t},\n\t\t\twant: \"[Fx] PROVIDE (PRIVATE)\t*bytes.Buffer <= bytes.NewBuffer() from module \\\"myModule\\\"\\n\",\n\t\t},\n\t\t{\n\t\t\tname: \"Replaced\",\n\t\t\tgive: &Replaced{\n\t\t\t\tOutputTypeNames: []string{\"*bytes.Buffer\"},\n\t\t\t\tStackTrace:      []string{\"main.main\", \"runtime.main\"},\n\t\t\t\tModuleTrace:     []string{\"main.main\"},\n\t\t\t},\n\t\t\twant: \"[Fx] REPLACE\t*bytes.Buffer\\n\",\n\t\t},\n\t\t{\n\t\t\tname: \"Replaced with module\",\n\t\t\tgive: &Replaced{\n\t\t\t\tModuleName:      \"myModule\",\n\t\t\t\tOutputTypeNames: []string{\"*bytes.Buffer\"},\n\t\t\t\tStackTrace:      []string{\"main.main\", \"runtime.main\"},\n\t\t\t\tModuleTrace:     []string{\"main.main\", \"mypackage.GetMyModule\"},\n\t\t\t},\n\t\t\twant: \"[Fx] REPLACE\t*bytes.Buffer from module \\\"myModule\\\"\\n\",\n\t\t},\n\t\t{\n\t\t\tname: \"ReplacedError\",\n\t\t\tgive: &Replaced{Err: errors.New(\"some error\")},\n\t\t\twant: \"[Fx] ERROR\tFailed to replace: some error\\n\",\n\t\t},\n\t\t{\n\t\t\tname: \"Decorated\",\n\t\t\tgive: &Decorated{\n\t\t\t\tDecoratorName:   \"bytes.NewBuffer()\",\n\t\t\t\tOutputTypeNames: []string{\"*bytes.Buffer\"},\n\t\t\t\tStackTrace:      []string{\"main.main\", \"runtime.main\"},\n\t\t\t\tModuleTrace:     []string{\"main.main\"},\n\t\t\t},\n\t\t\twant: \"[Fx] DECORATE\t*bytes.Buffer <= bytes.NewBuffer()\\n\",\n\t\t},\n\t\t{\n\t\t\tname: \"Decorated with module\",\n\t\t\tgive: &Decorated{\n\t\t\t\tDecoratorName:   \"bytes.NewBuffer()\",\n\t\t\t\tModuleName:      \"myModule\",\n\t\t\t\tOutputTypeNames: []string{\"*bytes.Buffer\"},\n\t\t\t\tStackTrace:      []string{\"main.main\", \"runtime.main\"},\n\t\t\t\tModuleTrace:     []string{\"main.main\", \"mypackage.GetMyModule\"},\n\t\t\t},\n\t\t\twant: \"[Fx] DECORATE\t*bytes.Buffer <= bytes.NewBuffer() from module \\\"myModule\\\"\\n\",\n\t\t},\n\t\t{\n\t\t\tname: \"DecorateError\",\n\t\t\tgive: &Decorated{Err: errors.New(\"some error\")},\n\t\t\twant: \"[Fx] Error after options were applied: some error\\n\",\n\t\t},\n\t\t{\n\t\t\tname: \"DecorateError/rich error\",\n\t\t\tgive: &Decorated{Err: &richError{}},\n\t\t\twant: \"[Fx] Error after options were applied: rich error\\n\",\n\t\t},\n\t\t{\n\t\t\tname: \"Run\",\n\t\t\tgive: &Run{Name: \"bytes.NewBuffer()\", Kind: \"constructor\", Runtime: 10 * time.Nanosecond},\n\t\t\twant: \"[Fx] RUN\\tconstructor: bytes.NewBuffer() in 10ns\\n\",\n\t\t},\n\t\t{\n\t\t\tname: \"Run with module\",\n\t\t\tgive: &Run{\n\t\t\t\tName:       \"bytes.NewBuffer()\",\n\t\t\t\tKind:       \"constructor\",\n\t\t\t\tRuntime:    50 * time.Millisecond,\n\t\t\t\tModuleName: \"myModule\",\n\t\t\t},\n\t\t\twant: \"[Fx] RUN\\tconstructor: bytes.NewBuffer() in 50ms from module \\\"myModule\\\"\\n\",\n\t\t},\n\t\t{\n\t\t\tname: \"RunError\",\n\t\t\tgive: &Run{\n\t\t\t\tName:    \"bytes.NewBuffer()\",\n\t\t\t\tKind:    \"constructor\",\n\t\t\t\tRuntime: 5 * time.Second,\n\t\t\t\tErr:     errors.New(\"terrible constructor error\"),\n\t\t\t},\n\t\t\twant: joinLines(\n\t\t\t\t\"[Fx] RUN\\tconstructor: bytes.NewBuffer() in 5s\",\n\t\t\t\t\"[Fx] Error returned: terrible constructor error\",\n\t\t\t),\n\t\t},\n\t\t{\n\t\t\tname: \"Invoking\",\n\t\t\tgive: &Invoking{FunctionName: \"bytes.NewBuffer()\"},\n\t\t\twant: \"[Fx] INVOKE\t\tbytes.NewBuffer()\\n\",\n\t\t},\n\t\t{\n\t\t\tname: \"Invoking with module\",\n\t\t\tgive: &Invoking{\n\t\t\t\tFunctionName: \"bytes.NewBuffer()\",\n\t\t\t\tModuleName:   \"myModule\",\n\t\t\t},\n\t\t\twant: \"[Fx] INVOKE\t\tbytes.NewBuffer() from module \\\"myModule\\\"\\n\",\n\t\t},\n\t\t{\n\t\t\tname: \"Invoked/Error\",\n\t\t\tgive: &Invoked{\n\t\t\t\tFunctionName: \"bytes.NewBuffer()\",\n\t\t\t\tErr:          errors.New(\"some error\"),\n\t\t\t\tTrace:        \"foo()\\n\\tbar/baz.go:42\\n\",\n\t\t\t},\n\t\t\twant: joinLines(\n\t\t\t\t\"[Fx] ERROR\t\tfx.Invoke(bytes.NewBuffer()) called from:\",\n\t\t\t\t\"foo()\",\n\t\t\t\t\"\tbar/baz.go:42\",\n\t\t\t\t\"Failed: some error\",\n\t\t\t),\n\t\t},\n\t\t{\n\t\t\tname: \"Invoked/Error/rich\",\n\t\t\tgive: &Invoked{\n\t\t\t\tFunctionName: \"bytes.NewBuffer()\",\n\t\t\t\tErr:          &richError{},\n\t\t\t\tTrace:        \"foo()\\n\\tbar/baz.go:42\\n\",\n\t\t\t},\n\t\t\twant: joinLines(\n\t\t\t\t\"[Fx] ERROR\t\tfx.Invoke(bytes.NewBuffer()) called from:\",\n\t\t\t\t\"foo()\",\n\t\t\t\t\"\tbar/baz.go:42\",\n\t\t\t\t\"Failed: rich error\",\n\t\t\t),\n\t\t},\n\t\t{\n\t\t\tname: \"StartError\",\n\t\t\tgive: &Started{Err: errors.New(\"some error\")},\n\t\t\twant: \"[Fx] ERROR\t\tFailed to start: some error\\n\",\n\t\t},\n\t\t{\n\t\t\tname: \"StartError/rich error\",\n\t\t\tgive: &Started{Err: &richError{}},\n\t\t\twant: \"[Fx] ERROR\t\tFailed to start: rich error\\n\",\n\t\t},\n\t\t{\n\t\t\tname: \"Stopping\",\n\t\t\tgive: &Stopping{Signal: os.Interrupt},\n\t\t\twant: \"[Fx] INTERRUPT\\n\",\n\t\t},\n\t\t{\n\t\t\tname: \"Stopped\",\n\t\t\tgive: &Stopped{Err: errors.New(\"some error\")},\n\t\t\twant: \"[Fx] ERROR\t\tFailed to stop cleanly: some error\\n\",\n\t\t},\n\t\t{\n\t\t\tname: \"Stopped/rich error\",\n\t\t\tgive: &Stopped{Err: &richError{}},\n\t\t\twant: \"[Fx] ERROR\t\tFailed to stop cleanly: rich error\\n\",\n\t\t},\n\t\t{\n\t\t\tname: \"RollingBack\",\n\t\t\tgive: &RollingBack{StartErr: errors.New(\"some error\")},\n\t\t\twant: \"[Fx] ERROR\t\tStart failed, rolling back: some error\\n\",\n\t\t},\n\t\t{\n\t\t\tname: \"RollingBack/rich error\",\n\t\t\tgive: &RollingBack{StartErr: &richError{}},\n\t\t\twant: \"[Fx] ERROR\t\tStart failed, rolling back: rich error\\n\",\n\t\t},\n\t\t{\n\t\t\tname: \"RolledBack\",\n\t\t\tgive: &RolledBack{Err: errors.New(\"some error\")},\n\t\t\twant: \"[Fx] ERROR\t\tCouldn't roll back cleanly: some error\\n\",\n\t\t},\n\t\t{\n\t\t\tname: \"RolledBack/rich error\",\n\t\t\tgive: &RolledBack{Err: &richError{}},\n\t\t\twant: \"[Fx] ERROR\t\tCouldn't roll back cleanly: rich error\\n\",\n\t\t},\n\t\t{\n\t\t\tname: \"Started\",\n\t\t\tgive: &Started{},\n\t\t\twant: \"[Fx] RUNNING\\n\",\n\t\t},\n\t\t{\n\t\t\tname: \"CustomLoggerError\",\n\t\t\tgive: &LoggerInitialized{Err: errors.New(\"great sadness\")},\n\t\t\twant: \"[Fx] ERROR\t\tFailed to initialize custom logger: great sadness\\n\",\n\t\t},\n\t\t{\n\t\t\tname: \"CustomLoggerError/rich error\",\n\t\t\tgive: &LoggerInitialized{Err: &richError{}},\n\t\t\twant: \"[Fx] ERROR\t\tFailed to initialize custom logger: rich error\\n\",\n\t\t},\n\t\t{\n\t\t\tname: \"LoggerInitialized\",\n\t\t\tgive: &LoggerInitialized{ConstructorName: \"go.uber.org/fx/fxevent.TestConsoleLogger.func1()\"},\n\t\t\twant: \"[Fx] LOGGER\tInitialized custom logger from go.uber.org/fx/fxevent.TestConsoleLogger.func1()\\n\",\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tt.Parallel()\n\n\t\t\tvar buff bytes.Buffer\n\t\t\t(&ConsoleLogger{W: &buff}).LogEvent(tt.give)\n\n\t\t\tassert.Equal(t, tt.want, buff.String())\n\t\t})\n\t}\n}\n\nfunc joinLines(lines ...string) string {\n\treturn strings.Join(lines, \"\\n\") + \"\\n\"\n}\n"
  },
  {
    "path": "fxevent/doc.go",
    "content": "// Copyright (c) 2024 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\n// Package fxevent defines a means of changing how Fx logs internal events.\n//\n// # Changing the Logger\n//\n// By default, the [ConsoleLogger] is used, writing readable logs to stderr.\n//\n// You can use the fx.WithLogger option to change this behavior\n// by providing a constructor that returns an alternative implementation\n// of the [Logger] interface.\n//\n//\tfx.WithLogger(func(cfg *Config) Logger {\n//\t\treturn &MyFxLogger{...}\n//\t})\n//\n// Because WithLogger accepts a constructor,\n// you can pull any other types and values from inside the container,\n// allowing use of the same logger that your application uses.\n//\n// For example, if you're using Zap inside your application,\n// you can use the [ZapLogger] implementation of the interface.\n//\n//\tfx.New(\n//\t\tfx.Provide(\n//\t\t\tzap.NewProduction, // provide a *zap.Logger\n//\t\t),\n//\t\tfx.WithLogger(\n//\t\t\tfunc(log *zap.Logger) fxevent.Logger {\n//\t\t\t\treturn &fxevent.ZapLogger{Logger: log}\n//\t\t\t},\n//\t\t),\n//\t)\n//\n// # Implementing a Custom Logger\n//\n// To implement a custom logger, you need to implement the [Logger] interface.\n// The Logger.LogEvent method accepts an [Event] object.\n//\n// [Event] is a union type that represents all the different events that Fx can emit.\n// You can use a type switch to handle each event type.\n// See 'event.go' for a list of all the possible events.\n//\n//\tfunc (l *MyFxLogger) LogEvent(e fxevent.Event) {\n//\t\tswitch e := e.(type) {\n//\t\tcase *fxevent.OnStartExecuting:\n//\t\t\t// ...\n//\t\t// ...\n//\t\t}\n//\t}\n//\n// The events contain enough information for observability and debugging purposes.\n// If you need more information in them,\n// feel free to open an issue to discuss the addition.\npackage fxevent\n"
  },
  {
    "path": "fxevent/event.go",
    "content": "// Copyright (c) 2021 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage fxevent\n\nimport (\n\t\"os\"\n\t\"time\"\n)\n\n// Event defines an event emitted by fx.\ntype Event interface {\n\tevent() // Only fxlog can implement this interface.\n}\n\n// Passing events by type to make Event hashable in the future.\nfunc (*OnStartExecuting) event()  {}\nfunc (*OnStartExecuted) event()   {}\nfunc (*OnStopExecuting) event()   {}\nfunc (*OnStopExecuted) event()    {}\nfunc (*Supplied) event()          {}\nfunc (*Provided) event()          {}\nfunc (*Replaced) event()          {}\nfunc (*Decorated) event()         {}\nfunc (*BeforeRun) event()         {}\nfunc (*Run) event()               {}\nfunc (*Invoking) event()          {}\nfunc (*Invoked) event()           {}\nfunc (*Stopping) event()          {}\nfunc (*Stopped) event()           {}\nfunc (*RollingBack) event()       {}\nfunc (*RolledBack) event()        {}\nfunc (*Started) event()           {}\nfunc (*LoggerInitialized) event() {}\n\n// OnStartExecuting is emitted before an OnStart hook is executed.\ntype OnStartExecuting struct {\n\t// FunctionName is the name of the function that will be executed.\n\tFunctionName string\n\n\t// CallerName is the name of the function that scheduled the hook for\n\t// execution.\n\tCallerName string\n}\n\n// OnStartExecuted is emitted after an OnStart hook has been executed.\ntype OnStartExecuted struct {\n\t// FunctionName is the name of the function that was executed.\n\tFunctionName string\n\n\t// CallerName is the name of the function that scheduled the hook for\n\t// execution.\n\tCallerName string\n\n\t// Method specifies the kind of the hook. This is one of \"OnStart\" and\n\t// \"OnStop\".\n\tMethod string\n\n\t// Runtime specifies how long it took to run this hook.\n\tRuntime time.Duration\n\n\t// Err is non-nil if the hook failed to execute.\n\tErr error\n}\n\n// OnStopExecuting is emitted before an OnStop hook is executed.\ntype OnStopExecuting struct {\n\t// FunctionName is the name of the function that will be executed.\n\tFunctionName string\n\n\t// CallerName is the name of the function that scheduled the hook for\n\t// execution.\n\tCallerName string\n}\n\n// OnStopExecuted is emitted after an OnStop hook has been executed.\ntype OnStopExecuted struct {\n\t// FunctionName is the name of the function that was executed.\n\tFunctionName string\n\n\t// CallerName is the name of the function that scheduled the hook for\n\t// execution.\n\tCallerName string\n\n\t// Runtime specifies how long it took to run this hook.\n\tRuntime time.Duration\n\n\t// Err is non-nil if the hook failed to execute.\n\tErr error\n}\n\n// Supplied is emitted after a value is added with fx.Supply.\ntype Supplied struct {\n\t// TypeName is the name of the type of value that was added.\n\tTypeName string\n\n\t// StackTrace is the stack trace of the call to Supply.\n\tStackTrace []string\n\n\t// ModuleTrace contains the module locations through which this value was added.\n\tModuleTrace []string\n\n\t// ModuleName is the name of the module in which the value was added to.\n\tModuleName string\n\n\t// Err is non-nil if we failed to supply the value.\n\tErr error\n}\n\n// Provided is emitted when a constructor is provided to Fx.\ntype Provided struct {\n\t// ConstructorName is the name of the constructor that was provided to\n\t// Fx.\n\tConstructorName string\n\n\t// StackTrace is the stack trace of where the constructor was provided to Fx.\n\tStackTrace []string\n\n\t// ModuleTrace contains the module locations through which this was provided to Fx.\n\tModuleTrace []string\n\n\t// OutputTypeNames is a list of names of types that are produced by\n\t// this constructor.\n\tOutputTypeNames []string\n\n\t// ModuleName is the name of the module in which the constructor was\n\t// provided to.\n\tModuleName string\n\n\t// Err is non-nil if we failed to provide this constructor.\n\tErr error\n\n\t// Private denotes whether the provided constructor is a [Private] constructor.\n\tPrivate bool\n}\n\n// Replaced is emitted when a value replaces a type in Fx.\ntype Replaced struct {\n\t// OutputTypeNames is a list of names of types that were replaced.\n\tOutputTypeNames []string\n\n\t// StackTrace is the stack trace of the call to Replace.\n\tStackTrace []string\n\n\t// ModuleTrace contains the module locations through which this value was added.\n\tModuleTrace []string\n\n\t// ModuleName is the name of the module in which the value was added to.\n\tModuleName string\n\n\t// Err is non-nil if we failed to supply the value.\n\tErr error\n}\n\n// Decorated is emitted when a decorator is executed in Fx.\ntype Decorated struct {\n\t// DecoratorName is the name of the decorator function that was\n\t// provided to Fx.\n\tDecoratorName string\n\n\t// StackTrace is the stack trace of where the decorator was given to Fx.\n\tStackTrace []string\n\n\t// ModuleTrace contains the module locations through which this value was added.\n\tModuleTrace []string\n\n\t// ModuleName is the name of the module in which the value was added to.\n\tModuleName string\n\n\t// OutputTypeNames is a list of names of types that are decorated by\n\t// this decorator.\n\tOutputTypeNames []string\n\n\t// Err is non-nil if we failed to run this decorator.\n\tErr error\n}\n\n// BeforeRun is emitted before a constructor, decorator, or supply/replace stub is run by Fx.\n// When complete, a Run will be emitted.\ntype BeforeRun struct {\n\t// Name is the name of the function that will be run.\n\tName string\n\n\t// Kind indicates which Fx option was used to pass along the function.\n\t// It is either \"provide\", \"decorate\", \"supply\", or \"replace\".\n\tKind string\n\n\t// ModuleName is the name of the module in which the function belongs.\n\tModuleName string\n}\n\n// Run is emitted after a constructor, decorator, or supply/replace stub is run by Fx.\ntype Run struct {\n\t// Name is the name of the function that was run.\n\tName string\n\n\t// Kind indicates which Fx option was used to pass along the function.\n\t// It is either \"provide\", \"decorate\", \"supply\", or \"replace\".\n\tKind string\n\n\t// ModuleName is the name of the module in which the function belongs.\n\tModuleName string\n\n\t// Runtime specifies how long it took to run this function.\n\tRuntime time.Duration\n\n\t// Err is non-nil if the function returned an error.\n\t// If fx.RecoverFromPanics is used, this will include panics.\n\tErr error\n}\n\n// Invoking is emitted before we invoke a function specified with fx.Invoke.\ntype Invoking struct {\n\t// FunctionName is the name of the function that will be invoked.\n\tFunctionName string\n\n\t// ModuleName is the name of the module in which the value was added to.\n\tModuleName string\n}\n\n// Invoked is emitted after we invoke a function specified with fx.Invoke,\n// whether it succeeded or failed.\ntype Invoked struct {\n\t// Functionname is the name of the function that was invoked.\n\tFunctionName string\n\n\t// ModuleName is the name of the module in which the value was added to.\n\tModuleName string\n\n\t// Err is non-nil if the function failed to execute.\n\tErr error\n\n\t// Trace records information about where the fx.Invoke call was made.\n\t// Note that this is NOT a stack trace of the error itself.\n\tTrace string\n}\n\n// Started is emitted when an application is started successfully and/or it\n// errored.\ntype Started struct {\n\t// Err is non-nil if the application failed to start successfully.\n\tErr error\n}\n\n// Stopping is emitted when the application receives a signal to shut down\n// after starting. This may happen with fx.Shutdowner or by sending a signal to\n// the application on the command line.\ntype Stopping struct {\n\t// Signal is the signal that caused this shutdown.\n\tSignal os.Signal\n}\n\n// Stopped is emitted when the application has finished shutting down, whether\n// successfully or not.\ntype Stopped struct {\n\t// Err is non-nil if errors were encountered during shutdown.\n\tErr error\n}\n\n// RollingBack is emitted when the application failed to start up due to an\n// error, and is being rolled back.\ntype RollingBack struct {\n\t// StartErr is the error that caused this rollback.\n\tStartErr error\n}\n\n// RolledBack is emitted after a service has been rolled back, whether it\n// succeeded or not.\ntype RolledBack struct {\n\t// Err is non-nil if the rollback failed.\n\tErr error\n}\n\n// LoggerInitialized is emitted when a logger supplied with fx.WithLogger is\n// instantiated, or if it fails to instantiate.\ntype LoggerInitialized struct {\n\t// ConstructorName is the name of the constructor that builds this\n\t// logger.\n\tConstructorName string\n\n\t// Err is non-nil if the logger failed to build.\n\tErr error\n}\n"
  },
  {
    "path": "fxevent/event_test.go",
    "content": "// Copyright (c) 2021 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage fxevent\n\nimport (\n\t\"testing\"\n\n\t\"go.uber.org/goleak\"\n)\n\n// TestForCoverage adds coverage for own sake.\nfunc TestForCoverage(t *testing.T) {\n\tt.Parallel()\n\n\tevents := []Event{\n\t\t&OnStartExecuting{},\n\t\t&OnStartExecuted{},\n\t\t&OnStopExecuting{},\n\t\t&OnStopExecuted{},\n\t\t&Supplied{},\n\t\t&Provided{},\n\t\t&Replaced{},\n\t\t&Decorated{},\n\t\t&BeforeRun{},\n\t\t&Run{},\n\t\t&Invoking{},\n\t\t&Invoked{},\n\t\t&Stopping{},\n\t\t&Stopped{},\n\t\t&RollingBack{},\n\t\t&RolledBack{},\n\t\t&Started{},\n\t\t&LoggerInitialized{},\n\t}\n\n\tfor _, e := range events {\n\t\te.event()\n\t}\n}\n\nfunc TestMain(m *testing.M) {\n\tgoleak.VerifyTestMain(m)\n}\n"
  },
  {
    "path": "fxevent/logger.go",
    "content": "// Copyright (c) 2021 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage fxevent\n\n// Logger defines interface used for logging.\ntype Logger interface {\n\t// LogEvent is called when a logging event is emitted.\n\tLogEvent(Event)\n}\n\n// NopLogger is an Fx event logger that ignores all messages.\nvar NopLogger = nopLogger{}\n\ntype nopLogger struct{}\n\nvar _ Logger = nopLogger{}\n\nfunc (nopLogger) LogEvent(Event) {}\n\nfunc (nopLogger) String() string { return \"NopLogger\" }\n"
  },
  {
    "path": "fxevent/slog.go",
    "content": "// Copyright (c) 2021 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\n//go:build go1.21\n\npackage fxevent\n\nimport (\n\t\"context\"\n\t\"log/slog\"\n\t\"strconv\"\n\t\"strings\"\n)\n\nvar _ Logger = (*SlogLogger)(nil)\n\n// SlogLogger an Fx event logger that logs events using a slog logger.\ntype SlogLogger struct {\n\tLogger *slog.Logger\n\n\tctx        context.Context\n\tlogLevel   slog.Level\n\terrorLevel *slog.Level\n}\n\n// UseContext sets the context that will be used when logging to slog.\nfunc (l *SlogLogger) UseContext(ctx context.Context) {\n\tl.ctx = ctx\n}\n\n// UseLogLevel sets the level of non-error logs emitted by Fx to level.\nfunc (l *SlogLogger) UseLogLevel(level slog.Level) {\n\tl.logLevel = level\n}\n\n// UseErrorLevel sets the level of error logs emitted by Fx to level.\nfunc (l *SlogLogger) UseErrorLevel(level slog.Level) {\n\tl.errorLevel = &level\n}\n\nfunc (l *SlogLogger) filter(fields []any) []any {\n\tfiltered := []any{}\n\n\tfor _, field := range fields {\n\t\tif field, ok := field.(slog.Attr); ok {\n\t\t\tif _, ok := field.Value.Any().(slogFieldSkip); ok {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t}\n\n\t\tfiltered = append(filtered, field)\n\t}\n\n\treturn filtered\n}\n\nfunc (l *SlogLogger) logEvent(msg string, fields ...any) {\n\tl.Logger.Log(l.ctx, l.logLevel, msg, l.filter(fields)...)\n}\n\nfunc (l *SlogLogger) logError(msg string, fields ...any) {\n\tlvl := slog.LevelError\n\tif l.errorLevel != nil {\n\t\tlvl = *l.errorLevel\n\t}\n\n\tl.Logger.Log(l.ctx, lvl, msg, l.filter(fields)...)\n}\n\n// LogEvent logs the given event to the provided Zap logger.\nfunc (l *SlogLogger) LogEvent(event Event) {\n\tswitch e := event.(type) {\n\tcase *OnStartExecuting:\n\t\tl.logEvent(\"OnStart hook executing\",\n\t\t\tslog.String(\"callee\", e.FunctionName),\n\t\t\tslog.String(\"caller\", e.CallerName),\n\t\t)\n\tcase *OnStartExecuted:\n\t\tif e.Err != nil {\n\t\t\tl.logError(\"OnStart hook failed\",\n\t\t\t\tslog.String(\"callee\", e.FunctionName),\n\t\t\t\tslog.String(\"caller\", e.CallerName),\n\t\t\t\tslogErr(e.Err),\n\t\t\t)\n\t\t} else {\n\t\t\tl.logEvent(\"OnStart hook executed\",\n\t\t\t\tslog.String(\"callee\", e.FunctionName),\n\t\t\t\tslog.String(\"caller\", e.CallerName),\n\t\t\t\tslog.String(\"runtime\", e.Runtime.String()),\n\t\t\t)\n\t\t}\n\tcase *OnStopExecuting:\n\t\tl.logEvent(\"OnStop hook executing\",\n\t\t\tslog.String(\"callee\", e.FunctionName),\n\t\t\tslog.String(\"caller\", e.CallerName),\n\t\t)\n\tcase *OnStopExecuted:\n\t\tif e.Err != nil {\n\t\t\tl.logError(\"OnStop hook failed\",\n\t\t\t\tslog.String(\"callee\", e.FunctionName),\n\t\t\t\tslog.String(\"caller\", e.CallerName),\n\t\t\t\tslogErr(e.Err),\n\t\t\t)\n\t\t} else {\n\t\t\tl.logEvent(\"OnStop hook executed\",\n\t\t\t\tslog.String(\"callee\", e.FunctionName),\n\t\t\t\tslog.String(\"caller\", e.CallerName),\n\t\t\t\tslog.String(\"runtime\", e.Runtime.String()),\n\t\t\t)\n\t\t}\n\tcase *Supplied:\n\t\tif e.Err != nil {\n\t\t\tl.logError(\"error encountered while applying options\",\n\t\t\t\tslog.String(\"type\", e.TypeName),\n\t\t\t\tslogStrings(\"moduletrace\", e.ModuleTrace),\n\t\t\t\tslogStrings(\"stacktrace\", e.StackTrace),\n\t\t\t\tslogMaybeModuleField(e.ModuleName),\n\t\t\t\tslogErr(e.Err))\n\t\t} else {\n\t\t\tl.logEvent(\"supplied\",\n\t\t\t\tslog.String(\"type\", e.TypeName),\n\t\t\t\tslogStrings(\"stacktrace\", e.StackTrace),\n\t\t\t\tslogStrings(\"moduletrace\", e.ModuleTrace),\n\t\t\t\tslogMaybeModuleField(e.ModuleName),\n\t\t\t)\n\t\t}\n\tcase *Provided:\n\t\tfor _, rtype := range e.OutputTypeNames {\n\t\t\tl.logEvent(\"provided\",\n\t\t\t\tslog.String(\"constructor\", e.ConstructorName),\n\t\t\t\tslogStrings(\"stacktrace\", e.StackTrace),\n\t\t\t\tslogStrings(\"moduletrace\", e.ModuleTrace),\n\t\t\t\tslogMaybeModuleField(e.ModuleName),\n\t\t\t\tslog.String(\"type\", rtype),\n\t\t\t\tslogMaybeBool(\"private\", e.Private),\n\t\t\t)\n\t\t}\n\t\tif e.Err != nil {\n\t\t\tl.logError(\"error encountered while applying options\",\n\t\t\t\tslogMaybeModuleField(e.ModuleName),\n\t\t\t\tslogStrings(\"stacktrace\", e.StackTrace),\n\t\t\t\tslogStrings(\"moduletrace\", e.ModuleTrace),\n\t\t\t\tslogErr(e.Err))\n\t\t}\n\tcase *Replaced:\n\t\tfor _, rtype := range e.OutputTypeNames {\n\t\t\tl.logEvent(\"replaced\",\n\t\t\t\tslogStrings(\"stacktrace\", e.StackTrace),\n\t\t\t\tslogStrings(\"moduletrace\", e.ModuleTrace),\n\t\t\t\tslogMaybeModuleField(e.ModuleName),\n\t\t\t\tslog.String(\"type\", rtype),\n\t\t\t)\n\t\t}\n\t\tif e.Err != nil {\n\t\t\tl.logError(\"error encountered while replacing\",\n\t\t\t\tslogStrings(\"stacktrace\", e.StackTrace),\n\t\t\t\tslogStrings(\"moduletrace\", e.ModuleTrace),\n\t\t\t\tslogMaybeModuleField(e.ModuleName),\n\t\t\t\tslogErr(e.Err))\n\t\t}\n\tcase *Decorated:\n\t\tfor _, rtype := range e.OutputTypeNames {\n\t\t\tl.logEvent(\"decorated\",\n\t\t\t\tslog.String(\"decorator\", e.DecoratorName),\n\t\t\t\tslogStrings(\"stacktrace\", e.StackTrace),\n\t\t\t\tslogStrings(\"moduletrace\", e.ModuleTrace),\n\t\t\t\tslogMaybeModuleField(e.ModuleName),\n\t\t\t\tslog.String(\"type\", rtype),\n\t\t\t)\n\t\t}\n\t\tif e.Err != nil {\n\t\t\tl.logError(\"error encountered while applying options\",\n\t\t\t\tslogStrings(\"stacktrace\", e.StackTrace),\n\t\t\t\tslogStrings(\"moduletrace\", e.ModuleTrace),\n\t\t\t\tslogMaybeModuleField(e.ModuleName),\n\t\t\t\tslogErr(e.Err))\n\t\t}\n\tcase *BeforeRun:\n\t\tl.logEvent(\"before run\",\n\t\t\tslog.String(\"name\", e.Name),\n\t\t\tslog.String(\"kind\", e.Kind),\n\t\t\tslogMaybeModuleField(e.ModuleName),\n\t\t)\n\tcase *Run:\n\t\tif e.Err != nil {\n\t\t\tl.logError(\"error returned\",\n\t\t\t\tslog.String(\"name\", e.Name),\n\t\t\t\tslog.String(\"kind\", e.Kind),\n\t\t\t\tslogMaybeModuleField(e.ModuleName),\n\t\t\t\tslogErr(e.Err),\n\t\t\t)\n\t\t} else {\n\t\t\tl.logEvent(\"run\",\n\t\t\t\tslog.String(\"name\", e.Name),\n\t\t\t\tslog.String(\"kind\", e.Kind),\n\t\t\t\tslog.String(\"runtime\", e.Runtime.String()),\n\t\t\t\tslogMaybeModuleField(e.ModuleName),\n\t\t\t)\n\t\t}\n\tcase *Invoking:\n\t\t// Do not log stack as it will make logs hard to read.\n\t\tl.logEvent(\"invoking\",\n\t\t\tslog.String(\"function\", e.FunctionName),\n\t\t\tslogMaybeModuleField(e.ModuleName),\n\t\t)\n\tcase *Invoked:\n\t\tif e.Err != nil {\n\t\t\tl.logError(\"invoke failed\",\n\t\t\t\tslogErr(e.Err),\n\t\t\t\tslog.String(\"stack\", e.Trace),\n\t\t\t\tslog.String(\"function\", e.FunctionName),\n\t\t\t\tslogMaybeModuleField(e.ModuleName),\n\t\t\t)\n\t\t}\n\tcase *Stopping:\n\t\tl.logEvent(\"received signal\",\n\t\t\tslog.String(\"signal\", strings.ToUpper(e.Signal.String())))\n\tcase *Stopped:\n\t\tif e.Err != nil {\n\t\t\tl.logError(\"stop failed\", slogErr(e.Err))\n\t\t}\n\tcase *RollingBack:\n\t\tl.logError(\"start failed, rolling back\", slogErr(e.StartErr))\n\tcase *RolledBack:\n\t\tif e.Err != nil {\n\t\t\tl.logError(\"rollback failed\", slogErr(e.Err))\n\t\t}\n\tcase *Started:\n\t\tif e.Err != nil {\n\t\t\tl.logError(\"start failed\", slogErr(e.Err))\n\t\t} else {\n\t\t\tl.logEvent(\"started\")\n\t\t}\n\tcase *LoggerInitialized:\n\t\tif e.Err != nil {\n\t\t\tl.logError(\"custom logger initialization failed\", slogErr(e.Err))\n\t\t} else {\n\t\t\tl.logEvent(\"initialized custom fxevent.Logger\", slog.String(\"function\", e.ConstructorName))\n\t\t}\n\t}\n}\n\ntype slogFieldSkip struct{}\n\nfunc slogMaybeModuleField(name string) slog.Attr {\n\tif len(name) == 0 {\n\t\treturn slog.Any(\"module\", slogFieldSkip{})\n\t}\n\treturn slog.String(\"module\", name)\n}\n\nfunc slogMaybeBool(name string, b bool) slog.Attr {\n\tif !b {\n\t\treturn slog.Any(name, slogFieldSkip{})\n\t}\n\treturn slog.Bool(name, true)\n}\n\nfunc slogErr(err error) slog.Attr {\n\treturn slog.String(\"error\", err.Error())\n}\n\nfunc slogStrings(key string, str []string) slog.Attr {\n\tattrs := make([]any, len(str))\n\tfor i, val := range str {\n\t\tattrs[i] = slog.String(strconv.Itoa(i), val)\n\t}\n\treturn slog.Group(key, attrs...)\n}\n"
  },
  {
    "path": "fxevent/slog_test.go",
    "content": "// Copyright (c) 2021 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\n//go:build go1.21\n\npackage fxevent\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"log/slog\"\n\t\"os\"\n\t\"strings\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n)\n\ntype slogObservableEntry struct {\n\trecord slog.Record\n}\n\nfunc (s slogObservableEntry) unwrap(attr slog.Attr, out map[string]any) {\n\tanyAttr := attr.Value.Any()\n\n\tsliceAttr, ok := anyAttr.([]slog.Attr)\n\tif !ok {\n\t\tout[attr.Key] = anyAttr\n\t\treturn\n\t}\n\n\tsliceAttrValues := make([]any, len(sliceAttr))\n\tfor i, iter := range sliceAttr {\n\t\tsliceAttrValues[i] = iter.Value.Any()\n\t}\n\n\tout[attr.Key] = sliceAttrValues\n}\n\nfunc (s slogObservableEntry) ContextMap() map[string]any {\n\tcontextMap := map[string]any{}\n\n\ts.record.Attrs(func(a slog.Attr) bool {\n\t\ts.unwrap(a, contextMap)\n\t\treturn true\n\t})\n\treturn contextMap\n}\n\ntype slogObservableLogger struct {\n\tlevel   slog.Level\n\tentries []slogObservableEntry\n\tattrs   []slog.Attr\n}\n\nfunc (s *slogObservableLogger) Enabled(ctx context.Context, level slog.Level) bool {\n\treturn int(s.level) <= int(level)\n}\n\nfunc (s *slogObservableLogger) Handle(ctx context.Context, record slog.Record) error {\n\ts.entries = append(s.entries, slogObservableEntry{record})\n\treturn nil\n}\n\nfunc (s *slogObservableLogger) WithAttrs(attrs []slog.Attr) slog.Handler {\n\treturn &slogObservableLogger{\n\t\tlevel:   s.level,\n\t\tentries: s.entries,\n\t\tattrs:   append(s.attrs, attrs...),\n\t}\n}\n\nfunc (s *slogObservableLogger) WithGroup(name string) slog.Handler {\n\treturn s\n}\n\nfunc (s *slogObservableLogger) TakeAll() []slogObservableEntry {\n\treturn s.entries\n}\n\nfunc newSlogObservableLogger(level slog.Level) (*slog.Logger, *slogObservableLogger) {\n\thandler := &slogObservableLogger{level: level}\n\treturn slog.New(handler), handler\n}\n\nfunc TestSlogLogger(t *testing.T) {\n\tt.Parallel()\n\n\tsomeError := errors.New(\"some error\")\n\n\ttests := []struct {\n\t\tname        string\n\t\tgive        Event\n\t\twantMessage string\n\t\twantFields  map[string]any\n\t}{\n\t\t{\n\t\t\tname: \"OnStartExecuting\",\n\t\t\tgive: &OnStartExecuting{\n\t\t\t\tFunctionName: \"hook.onStart\",\n\t\t\t\tCallerName:   \"bytes.NewBuffer\",\n\t\t\t},\n\t\t\twantMessage: \"OnStart hook executing\",\n\t\t\twantFields: map[string]any{\n\t\t\t\t\"caller\": \"bytes.NewBuffer\",\n\t\t\t\t\"callee\": \"hook.onStart\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"OnStopExecuting\",\n\t\t\tgive: &OnStopExecuting{\n\t\t\t\tFunctionName: \"hook.onStop1\",\n\t\t\t\tCallerName:   \"bytes.NewBuffer\",\n\t\t\t},\n\t\t\twantMessage: \"OnStop hook executing\",\n\t\t\twantFields: map[string]any{\n\t\t\t\t\"caller\": \"bytes.NewBuffer\",\n\t\t\t\t\"callee\": \"hook.onStop1\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"OnStopExecuted/Error\",\n\t\t\tgive: &OnStopExecuted{\n\t\t\t\tFunctionName: \"hook.onStart1\",\n\t\t\t\tCallerName:   \"bytes.NewBuffer\",\n\t\t\t\tErr:          fmt.Errorf(\"some error\"),\n\t\t\t},\n\t\t\twantMessage: \"OnStop hook failed\",\n\t\t\twantFields: map[string]any{\n\t\t\t\t\"caller\": \"bytes.NewBuffer\",\n\t\t\t\t\"callee\": \"hook.onStart1\",\n\t\t\t\t\"error\":  \"some error\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"OnStopExecuted\",\n\t\t\tgive: &OnStopExecuted{\n\t\t\t\tFunctionName: \"hook.onStart1\",\n\t\t\t\tCallerName:   \"bytes.NewBuffer\",\n\t\t\t\tRuntime:      time.Millisecond * 3,\n\t\t\t},\n\t\t\twantMessage: \"OnStop hook executed\",\n\t\t\twantFields: map[string]any{\n\t\t\t\t\"caller\":  \"bytes.NewBuffer\",\n\t\t\t\t\"callee\":  \"hook.onStart1\",\n\t\t\t\t\"runtime\": \"3ms\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"OnStartExecuted/Error\",\n\t\t\tgive: &OnStartExecuted{\n\t\t\t\tFunctionName: \"hook.onStart1\",\n\t\t\t\tCallerName:   \"bytes.NewBuffer\",\n\t\t\t\tErr:          fmt.Errorf(\"some error\"),\n\t\t\t},\n\t\t\twantMessage: \"OnStart hook failed\",\n\t\t\twantFields: map[string]any{\n\t\t\t\t\"caller\": \"bytes.NewBuffer\",\n\t\t\t\t\"callee\": \"hook.onStart1\",\n\t\t\t\t\"error\":  \"some error\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"OnStartExecuted\",\n\t\t\tgive: &OnStartExecuted{\n\t\t\t\tFunctionName: \"hook.onStart1\",\n\t\t\t\tCallerName:   \"bytes.NewBuffer\",\n\t\t\t\tRuntime:      time.Millisecond * 3,\n\t\t\t},\n\t\t\twantMessage: \"OnStart hook executed\",\n\t\t\twantFields: map[string]any{\n\t\t\t\t\"caller\":  \"bytes.NewBuffer\",\n\t\t\t\t\"callee\":  \"hook.onStart1\",\n\t\t\t\t\"runtime\": \"3ms\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"Supplied\",\n\t\t\tgive: &Supplied{\n\t\t\t\tTypeName:    \"*bytes.Buffer\",\n\t\t\t\tStackTrace:  []string{\"main.main\", \"runtime.main\"},\n\t\t\t\tModuleTrace: []string{\"main.main\"},\n\t\t\t},\n\t\t\twantMessage: \"supplied\",\n\t\t\twantFields: map[string]any{\n\t\t\t\t\"type\":        \"*bytes.Buffer\",\n\t\t\t\t\"stacktrace\":  []any{\"main.main\", \"runtime.main\"},\n\t\t\t\t\"moduletrace\": []any{\"main.main\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"Supplied/Error\",\n\t\t\tgive: &Supplied{\n\t\t\t\tTypeName:    \"*bytes.Buffer\",\n\t\t\t\tStackTrace:  []string{\"main.main\", \"runtime.main\"},\n\t\t\t\tModuleTrace: []string{\"main.main\"},\n\t\t\t\tErr:         someError,\n\t\t\t},\n\t\t\twantMessage: \"error encountered while applying options\",\n\t\t\twantFields: map[string]any{\n\t\t\t\t\"type\":        \"*bytes.Buffer\",\n\t\t\t\t\"stacktrace\":  []any{\"main.main\", \"runtime.main\"},\n\t\t\t\t\"moduletrace\": []any{\"main.main\"},\n\t\t\t\t\"error\":       \"some error\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"Provide\",\n\t\t\tgive: &Provided{\n\t\t\t\tConstructorName: \"bytes.NewBuffer()\",\n\t\t\t\tStackTrace:      []string{\"main.main\", \"runtime.main\"},\n\t\t\t\tModuleTrace:     []string{\"main.main\"},\n\t\t\t\tModuleName:      \"myModule\",\n\t\t\t\tOutputTypeNames: []string{\"*bytes.Buffer\"},\n\t\t\t\tPrivate:         false,\n\t\t\t},\n\t\t\twantMessage: \"provided\",\n\t\t\twantFields: map[string]any{\n\t\t\t\t\"constructor\": \"bytes.NewBuffer()\",\n\t\t\t\t\"stacktrace\":  []any{\"main.main\", \"runtime.main\"},\n\t\t\t\t\"moduletrace\": []any{\"main.main\"},\n\t\t\t\t\"type\":        \"*bytes.Buffer\",\n\t\t\t\t\"module\":      \"myModule\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"PrivateProvide\",\n\t\t\tgive: &Provided{\n\t\t\t\tConstructorName: \"bytes.NewBuffer()\",\n\t\t\t\tStackTrace:      []string{\"main.main\", \"runtime.main\"},\n\t\t\t\tModuleTrace:     []string{\"main.main\"},\n\t\t\t\tModuleName:      \"myModule\",\n\t\t\t\tOutputTypeNames: []string{\"*bytes.Buffer\"},\n\t\t\t\tPrivate:         true,\n\t\t\t},\n\t\t\twantMessage: \"provided\",\n\t\t\twantFields: map[string]any{\n\t\t\t\t\"constructor\": \"bytes.NewBuffer()\",\n\t\t\t\t\"stacktrace\":  []any{\"main.main\", \"runtime.main\"},\n\t\t\t\t\"moduletrace\": []any{\"main.main\"},\n\t\t\t\t\"type\":        \"*bytes.Buffer\",\n\t\t\t\t\"module\":      \"myModule\",\n\t\t\t\t\"private\":     true,\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"Provide/Error\",\n\t\t\tgive: &Provided{\n\t\t\t\tStackTrace:  []string{\"main.main\", \"runtime.main\"},\n\t\t\t\tModuleTrace: []string{\"main.main\"},\n\t\t\t\tErr:         someError,\n\t\t\t},\n\t\t\twantMessage: \"error encountered while applying options\",\n\t\t\twantFields: map[string]any{\n\t\t\t\t\"stacktrace\":  []any{\"main.main\", \"runtime.main\"},\n\t\t\t\t\"moduletrace\": []any{\"main.main\"},\n\t\t\t\t\"error\":       \"some error\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"Replace\",\n\t\t\tgive: &Replaced{\n\t\t\t\tModuleName:      \"myModule\",\n\t\t\t\tStackTrace:      []string{\"main.main\", \"runtime.main\"},\n\t\t\t\tModuleTrace:     []string{\"main.main\"},\n\t\t\t\tOutputTypeNames: []string{\"*bytes.Buffer\"},\n\t\t\t},\n\t\t\twantMessage: \"replaced\",\n\t\t\twantFields: map[string]any{\n\t\t\t\t\"type\":        \"*bytes.Buffer\",\n\t\t\t\t\"stacktrace\":  []any{\"main.main\", \"runtime.main\"},\n\t\t\t\t\"moduletrace\": []any{\"main.main\"},\n\t\t\t\t\"module\":      \"myModule\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"Replace/Error\",\n\t\t\tgive: &Replaced{\n\t\t\t\tStackTrace:  []string{\"main.main\", \"runtime.main\"},\n\t\t\t\tModuleTrace: []string{\"main.main\"},\n\t\t\t\tErr:         someError,\n\t\t\t},\n\n\t\t\twantMessage: \"error encountered while replacing\",\n\t\t\twantFields: map[string]any{\n\t\t\t\t\"stacktrace\":  []any{\"main.main\", \"runtime.main\"},\n\t\t\t\t\"moduletrace\": []any{\"main.main\"},\n\t\t\t\t\"error\":       \"some error\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"Decorate\",\n\t\t\tgive: &Decorated{\n\t\t\t\tDecoratorName:   \"bytes.NewBuffer()\",\n\t\t\t\tStackTrace:      []string{\"main.main\", \"runtime.main\"},\n\t\t\t\tModuleTrace:     []string{\"main.main\"},\n\t\t\t\tModuleName:      \"myModule\",\n\t\t\t\tOutputTypeNames: []string{\"*bytes.Buffer\"},\n\t\t\t},\n\t\t\twantMessage: \"decorated\",\n\t\t\twantFields: map[string]any{\n\t\t\t\t\"decorator\":   \"bytes.NewBuffer()\",\n\t\t\t\t\"stacktrace\":  []any{\"main.main\", \"runtime.main\"},\n\t\t\t\t\"moduletrace\": []any{\"main.main\"},\n\t\t\t\t\"type\":        \"*bytes.Buffer\",\n\t\t\t\t\"module\":      \"myModule\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"Decorate/Error\",\n\t\t\tgive: &Decorated{\n\t\t\t\tStackTrace:  []string{\"main.main\", \"runtime.main\"},\n\t\t\t\tModuleTrace: []string{\"main.main\"},\n\t\t\t\tErr:         someError,\n\t\t\t},\n\t\t\twantMessage: \"error encountered while applying options\",\n\t\t\twantFields: map[string]any{\n\t\t\t\t\"stacktrace\":  []any{\"main.main\", \"runtime.main\"},\n\t\t\t\t\"moduletrace\": []any{\"main.main\"},\n\t\t\t\t\"error\":       \"some error\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:        \"BeforeRun\",\n\t\t\tgive:        &BeforeRun{Name: \"bytes.NewBuffer()\", Kind: \"constructor\"},\n\t\t\twantMessage: \"before run\",\n\t\t\twantFields: map[string]any{\n\t\t\t\t\"name\": \"bytes.NewBuffer()\",\n\t\t\t\t\"kind\": \"constructor\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:        \"Run\",\n\t\t\tgive:        &Run{Name: \"bytes.NewBuffer()\", Kind: \"constructor\", Runtime: 3 * time.Millisecond},\n\t\t\twantMessage: \"run\",\n\t\t\twantFields: map[string]any{\n\t\t\t\t\"name\":    \"bytes.NewBuffer()\",\n\t\t\t\t\"kind\":    \"constructor\",\n\t\t\t\t\"runtime\": \"3ms\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"Run with module\",\n\t\t\tgive: &Run{\n\t\t\t\tName:       \"bytes.NewBuffer()\",\n\t\t\t\tKind:       \"constructor\",\n\t\t\t\tModuleName: \"myModule\",\n\t\t\t\tRuntime:    3 * time.Millisecond,\n\t\t\t},\n\t\t\twantMessage: \"run\",\n\t\t\twantFields: map[string]any{\n\t\t\t\t\"name\":    \"bytes.NewBuffer()\",\n\t\t\t\t\"kind\":    \"constructor\",\n\t\t\t\t\"module\":  \"myModule\",\n\t\t\t\t\"runtime\": \"3ms\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"Run/Error\",\n\t\t\tgive: &Run{\n\t\t\t\tName: \"bytes.NewBuffer()\",\n\t\t\t\tKind: \"constructor\",\n\t\t\t\tErr:  someError,\n\t\t\t},\n\t\t\twantMessage: \"error returned\",\n\t\t\twantFields: map[string]any{\n\t\t\t\t\"name\":  \"bytes.NewBuffer()\",\n\t\t\t\t\"kind\":  \"constructor\",\n\t\t\t\t\"error\": \"some error\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:        \"Invoking/Success\",\n\t\t\tgive:        &Invoking{ModuleName: \"myModule\", FunctionName: \"bytes.NewBuffer()\"},\n\t\t\twantMessage: \"invoking\",\n\t\t\twantFields: map[string]any{\n\t\t\t\t\"function\": \"bytes.NewBuffer()\",\n\t\t\t\t\"module\":   \"myModule\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:        \"Invoked/Error\",\n\t\t\tgive:        &Invoked{FunctionName: \"bytes.NewBuffer()\", Err: someError},\n\t\t\twantMessage: \"invoke failed\",\n\t\t\twantFields: map[string]any{\n\t\t\t\t\"error\":    \"some error\",\n\t\t\t\t\"stack\":    \"\",\n\t\t\t\t\"function\": \"bytes.NewBuffer()\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:        \"Start/Error\",\n\t\t\tgive:        &Started{Err: someError},\n\t\t\twantMessage: \"start failed\",\n\t\t\twantFields: map[string]any{\n\t\t\t\t\"error\": \"some error\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:        \"Stopping\",\n\t\t\tgive:        &Stopping{Signal: os.Interrupt},\n\t\t\twantMessage: \"received signal\",\n\t\t\twantFields: map[string]any{\n\t\t\t\t\"signal\": \"INTERRUPT\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:        \"Stopped/Error\",\n\t\t\tgive:        &Stopped{Err: someError},\n\t\t\twantMessage: \"stop failed\",\n\t\t\twantFields: map[string]any{\n\t\t\t\t\"error\": \"some error\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:        \"RollingBack/Error\",\n\t\t\tgive:        &RollingBack{StartErr: someError},\n\t\t\twantMessage: \"start failed, rolling back\",\n\t\t\twantFields: map[string]any{\n\t\t\t\t\"error\": \"some error\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:        \"RolledBack/Error\",\n\t\t\tgive:        &RolledBack{Err: someError},\n\t\t\twantMessage: \"rollback failed\",\n\t\t\twantFields: map[string]any{\n\t\t\t\t\"error\": \"some error\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:        \"Started\",\n\t\t\tgive:        &Started{},\n\t\t\twantMessage: \"started\",\n\t\t\twantFields:  map[string]any{},\n\t\t},\n\t\t{\n\t\t\tname:        \"LoggerInitialized/Error\",\n\t\t\tgive:        &LoggerInitialized{Err: someError},\n\t\t\twantMessage: \"custom logger initialization failed\",\n\t\t\twantFields: map[string]any{\n\t\t\t\t\"error\": \"some error\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:        \"LoggerInitialized\",\n\t\t\tgive:        &LoggerInitialized{ConstructorName: \"bytes.NewBuffer()\"},\n\t\t\twantMessage: \"initialized custom fxevent.Logger\",\n\t\t\twantFields: map[string]any{\n\t\t\t\t\"function\": \"bytes.NewBuffer()\",\n\t\t\t},\n\t\t},\n\t}\n\n\tt.Run(\"debug observer, log at default (info)\", func(t *testing.T) {\n\t\tfor _, tt := range tests {\n\t\t\ttt := tt\n\t\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\t\tt.Parallel()\n\n\t\t\t\tcore, observedLogs := newSlogObservableLogger(slog.LevelDebug)\n\t\t\t\t(&SlogLogger{Logger: core}).LogEvent(tt.give)\n\n\t\t\t\tlogs := observedLogs.TakeAll()\n\t\t\t\trequire.Len(t, logs, 1)\n\t\t\t\tgot := logs[0]\n\n\t\t\t\tassert.Equal(t, tt.wantMessage, got.record.Message)\n\t\t\t\tassert.Equal(t, tt.wantFields, got.ContextMap())\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"info observer, log at debug\", func(t *testing.T) {\n\t\tfor _, tt := range tests {\n\t\t\ttt := tt\n\t\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\t\tt.Parallel()\n\n\t\t\t\tcore, observedLogs := newSlogObservableLogger(slog.LevelInfo)\n\t\t\t\tl := &SlogLogger{Logger: core}\n\t\t\t\tl.UseLogLevel(slog.LevelDebug)\n\t\t\t\tl.LogEvent(tt.give)\n\n\t\t\t\tlogs := observedLogs.TakeAll()\n\t\t\t\t// logs are not visible unless they are errors\n\t\t\t\tif strings.HasSuffix(tt.name, \"/Error\") {\n\t\t\t\t\trequire.Len(t, logs, 1)\n\t\t\t\t\tgot := logs[0]\n\t\t\t\t\tassert.Equal(t, tt.wantMessage, got.record.Message)\n\t\t\t\t\tassert.Equal(t, tt.wantFields, got.ContextMap())\n\t\t\t\t} else {\n\t\t\t\t\trequire.Len(t, logs, 0)\n\t\t\t\t}\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"info observer, log/error at debug\", func(t *testing.T) {\n\t\tfor _, tt := range tests {\n\t\t\ttt := tt\n\t\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\t\tt.Parallel()\n\n\t\t\t\tcore, observedLogs := newSlogObservableLogger(slog.LevelInfo)\n\t\t\t\tl := &SlogLogger{Logger: core}\n\t\t\t\tl.UseLogLevel(slog.LevelDebug)\n\t\t\t\tl.UseErrorLevel(slog.LevelDebug)\n\t\t\t\tl.LogEvent(tt.give)\n\n\t\t\t\tlogs := observedLogs.TakeAll()\n\t\t\t\trequire.Len(t, logs, 0, \"no logs should be visible\")\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"test setting log levels\", func(t *testing.T) {\n\t\tlevels := []slog.Level{\n\t\t\tslog.LevelError,\n\t\t\tslog.LevelDebug,\n\t\t\tslog.LevelWarn,\n\t\t\tslog.LevelInfo,\n\t\t}\n\n\t\tfor _, level := range levels {\n\t\t\tcore, observedLogs := newSlogObservableLogger(level)\n\t\t\tlogger := &SlogLogger{Logger: core}\n\t\t\tlogger.UseLogLevel(level)\n\t\t\tfunc() {\n\t\t\t\tdefer func() {\n\t\t\t\t\trecover()\n\t\t\t\t}()\n\t\t\t\tlogger.LogEvent(&OnStartExecuting{\n\t\t\t\t\tFunctionName: \"hook.onStart\",\n\t\t\t\t\tCallerName:   \"bytes.NewBuffer\",\n\t\t\t\t})\n\t\t\t}()\n\t\t\tlogs := observedLogs.TakeAll()\n\t\t\trequire.Len(t, logs, 1)\n\t\t}\n\t})\n\n\tt.Run(\"test setting error log levels\", func(t *testing.T) {\n\t\tlevels := []slog.Level{\n\t\t\tslog.LevelError,\n\t\t\tslog.LevelDebug,\n\t\t\tslog.LevelWarn,\n\t\t\tslog.LevelInfo,\n\t\t}\n\n\t\tfor _, level := range levels {\n\t\t\tcore, observedLogs := newSlogObservableLogger(level)\n\t\t\tlogger := &SlogLogger{Logger: core}\n\t\t\tlogger.UseErrorLevel(level)\n\t\t\tfunc() {\n\t\t\t\tdefer func() {\n\t\t\t\t\trecover()\n\t\t\t\t}()\n\t\t\t\tlogger.LogEvent(&OnStopExecuted{\n\t\t\t\t\tFunctionName: \"hook.onStart1\",\n\t\t\t\t\tCallerName:   \"bytes.NewBuffer\",\n\t\t\t\t\tErr:          fmt.Errorf(\"some error\"),\n\t\t\t\t})\n\t\t\t}()\n\t\t\tlogs := observedLogs.TakeAll()\n\t\t\trequire.Len(t, logs, 1)\n\t\t}\n\t})\n}\n"
  },
  {
    "path": "fxevent/zap.go",
    "content": "// Copyright (c) 2021 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage fxevent\n\nimport (\n\t\"strings\"\n\n\t\"go.uber.org/zap\"\n\t\"go.uber.org/zap/zapcore\"\n)\n\n// ZapLogger is an Fx event logger that logs events to Zap.\ntype ZapLogger struct {\n\tLogger *zap.Logger\n\n\tlogLevel   zapcore.Level // default: zapcore.InfoLevel\n\terrorLevel *zapcore.Level\n}\n\nvar _ Logger = (*ZapLogger)(nil)\n\n// UseErrorLevel sets the level of error logs emitted by Fx to level.\nfunc (l *ZapLogger) UseErrorLevel(level zapcore.Level) {\n\tl.errorLevel = &level\n}\n\n// UseLogLevel sets the level of non-error logs emitted by Fx to level.\nfunc (l *ZapLogger) UseLogLevel(level zapcore.Level) {\n\tl.logLevel = level\n}\n\nfunc (l *ZapLogger) logEvent(msg string, fields ...zap.Field) {\n\tl.Logger.Log(l.logLevel, msg, fields...)\n}\n\nfunc (l *ZapLogger) logError(msg string, fields ...zap.Field) {\n\tlvl := zapcore.ErrorLevel\n\tif l.errorLevel != nil {\n\t\tlvl = *l.errorLevel\n\t}\n\tl.Logger.Log(lvl, msg, fields...)\n}\n\n// LogEvent logs the given event to the provided Zap logger.\nfunc (l *ZapLogger) LogEvent(event Event) {\n\tswitch e := event.(type) {\n\tcase *OnStartExecuting:\n\t\tl.logEvent(\"OnStart hook executing\",\n\t\t\tzap.String(\"callee\", e.FunctionName),\n\t\t\tzap.String(\"caller\", e.CallerName),\n\t\t)\n\tcase *OnStartExecuted:\n\t\tif e.Err != nil {\n\t\t\tl.logError(\"OnStart hook failed\",\n\t\t\t\tzap.String(\"callee\", e.FunctionName),\n\t\t\t\tzap.String(\"caller\", e.CallerName),\n\t\t\t\tzap.Error(e.Err),\n\t\t\t)\n\t\t} else {\n\t\t\tl.logEvent(\"OnStart hook executed\",\n\t\t\t\tzap.String(\"callee\", e.FunctionName),\n\t\t\t\tzap.String(\"caller\", e.CallerName),\n\t\t\t\tzap.String(\"runtime\", e.Runtime.String()),\n\t\t\t)\n\t\t}\n\tcase *OnStopExecuting:\n\t\tl.logEvent(\"OnStop hook executing\",\n\t\t\tzap.String(\"callee\", e.FunctionName),\n\t\t\tzap.String(\"caller\", e.CallerName),\n\t\t)\n\tcase *OnStopExecuted:\n\t\tif e.Err != nil {\n\t\t\tl.logError(\"OnStop hook failed\",\n\t\t\t\tzap.String(\"callee\", e.FunctionName),\n\t\t\t\tzap.String(\"caller\", e.CallerName),\n\t\t\t\tzap.Error(e.Err),\n\t\t\t)\n\t\t} else {\n\t\t\tl.logEvent(\"OnStop hook executed\",\n\t\t\t\tzap.String(\"callee\", e.FunctionName),\n\t\t\t\tzap.String(\"caller\", e.CallerName),\n\t\t\t\tzap.String(\"runtime\", e.Runtime.String()),\n\t\t\t)\n\t\t}\n\tcase *Supplied:\n\t\tif e.Err != nil {\n\t\t\tl.logError(\"error encountered while applying options\",\n\t\t\t\tzap.String(\"type\", e.TypeName),\n\t\t\t\tzap.Strings(\"stacktrace\", e.StackTrace),\n\t\t\t\tzap.Strings(\"moduletrace\", e.ModuleTrace),\n\t\t\t\tmoduleField(e.ModuleName),\n\t\t\t\tzap.Error(e.Err))\n\t\t} else {\n\t\t\tl.logEvent(\"supplied\",\n\t\t\t\tzap.String(\"type\", e.TypeName),\n\t\t\t\tzap.Strings(\"stacktrace\", e.StackTrace),\n\t\t\t\tzap.Strings(\"moduletrace\", e.ModuleTrace),\n\t\t\t\tmoduleField(e.ModuleName),\n\t\t\t)\n\t\t}\n\tcase *Provided:\n\t\tfor _, rtype := range e.OutputTypeNames {\n\t\t\tl.logEvent(\"provided\",\n\t\t\t\tzap.String(\"constructor\", e.ConstructorName),\n\t\t\t\tzap.Strings(\"stacktrace\", e.StackTrace),\n\t\t\t\tzap.Strings(\"moduletrace\", e.ModuleTrace),\n\t\t\t\tmoduleField(e.ModuleName),\n\t\t\t\tzap.String(\"type\", rtype),\n\t\t\t\tmaybeBool(\"private\", e.Private),\n\t\t\t)\n\t\t}\n\t\tif e.Err != nil {\n\t\t\tl.logError(\"error encountered while applying options\",\n\t\t\t\tmoduleField(e.ModuleName),\n\t\t\t\tzap.Strings(\"stacktrace\", e.StackTrace),\n\t\t\t\tzap.Strings(\"moduletrace\", e.ModuleTrace),\n\t\t\t\tzap.Error(e.Err))\n\t\t}\n\tcase *Replaced:\n\t\tfor _, rtype := range e.OutputTypeNames {\n\t\t\tl.logEvent(\"replaced\",\n\t\t\t\tzap.Strings(\"stacktrace\", e.StackTrace),\n\t\t\t\tzap.Strings(\"moduletrace\", e.ModuleTrace),\n\t\t\t\tmoduleField(e.ModuleName),\n\t\t\t\tzap.String(\"type\", rtype),\n\t\t\t)\n\t\t}\n\t\tif e.Err != nil {\n\t\t\tl.logError(\"error encountered while replacing\",\n\t\t\t\tzap.Strings(\"stacktrace\", e.StackTrace),\n\t\t\t\tzap.Strings(\"moduletrace\", e.ModuleTrace),\n\t\t\t\tmoduleField(e.ModuleName),\n\t\t\t\tzap.Error(e.Err))\n\t\t}\n\tcase *Decorated:\n\t\tfor _, rtype := range e.OutputTypeNames {\n\t\t\tl.logEvent(\"decorated\",\n\t\t\t\tzap.String(\"decorator\", e.DecoratorName),\n\t\t\t\tzap.Strings(\"stacktrace\", e.StackTrace),\n\t\t\t\tzap.Strings(\"moduletrace\", e.ModuleTrace),\n\t\t\t\tmoduleField(e.ModuleName),\n\t\t\t\tzap.String(\"type\", rtype),\n\t\t\t)\n\t\t}\n\t\tif e.Err != nil {\n\t\t\tl.logError(\"error encountered while applying options\",\n\t\t\t\tzap.Strings(\"stacktrace\", e.StackTrace),\n\t\t\t\tzap.Strings(\"moduletrace\", e.ModuleTrace),\n\t\t\t\tmoduleField(e.ModuleName),\n\t\t\t\tzap.Error(e.Err))\n\t\t}\n\tcase *BeforeRun:\n\t\tl.logEvent(\"before run\",\n\t\t\tzap.String(\"name\", e.Name),\n\t\t\tzap.String(\"kind\", e.Kind),\n\t\t\tmoduleField(e.ModuleName),\n\t\t)\n\tcase *Run:\n\t\tif e.Err != nil {\n\t\t\tl.logError(\"error returned\",\n\t\t\t\tzap.String(\"name\", e.Name),\n\t\t\t\tzap.String(\"kind\", e.Kind),\n\t\t\t\tmoduleField(e.ModuleName),\n\t\t\t\tzap.Error(e.Err),\n\t\t\t)\n\t\t} else {\n\t\t\tl.logEvent(\"run\",\n\t\t\t\tzap.String(\"name\", e.Name),\n\t\t\t\tzap.String(\"kind\", e.Kind),\n\t\t\t\tzap.String(\"runtime\", e.Runtime.String()),\n\t\t\t\tmoduleField(e.ModuleName),\n\t\t\t)\n\t\t}\n\tcase *Invoking:\n\t\t// Do not log stack as it will make logs hard to read.\n\t\tl.logEvent(\"invoking\",\n\t\t\tzap.String(\"function\", e.FunctionName),\n\t\t\tmoduleField(e.ModuleName),\n\t\t)\n\tcase *Invoked:\n\t\tif e.Err != nil {\n\t\t\tl.logError(\"invoke failed\",\n\t\t\t\tzap.Error(e.Err),\n\t\t\t\tzap.String(\"stack\", e.Trace),\n\t\t\t\tzap.String(\"function\", e.FunctionName),\n\t\t\t\tmoduleField(e.ModuleName),\n\t\t\t)\n\t\t}\n\tcase *Stopping:\n\t\tl.logEvent(\"received signal\",\n\t\t\tzap.String(\"signal\", strings.ToUpper(e.Signal.String())))\n\tcase *Stopped:\n\t\tif e.Err != nil {\n\t\t\tl.logError(\"stop failed\", zap.Error(e.Err))\n\t\t}\n\tcase *RollingBack:\n\t\tl.logError(\"start failed, rolling back\", zap.Error(e.StartErr))\n\tcase *RolledBack:\n\t\tif e.Err != nil {\n\t\t\tl.logError(\"rollback failed\", zap.Error(e.Err))\n\t\t}\n\tcase *Started:\n\t\tif e.Err != nil {\n\t\t\tl.logError(\"start failed\", zap.Error(e.Err))\n\t\t} else {\n\t\t\tl.logEvent(\"started\")\n\t\t}\n\tcase *LoggerInitialized:\n\t\tif e.Err != nil {\n\t\t\tl.logError(\"custom logger initialization failed\", zap.Error(e.Err))\n\t\t} else {\n\t\t\tl.logEvent(\"initialized custom fxevent.Logger\", zap.String(\"function\", e.ConstructorName))\n\t\t}\n\t}\n}\n\nfunc moduleField(name string) zap.Field {\n\tif len(name) == 0 {\n\t\treturn zap.Skip()\n\t}\n\treturn zap.String(\"module\", name)\n}\n\nfunc maybeBool(name string, b bool) zap.Field {\n\tif b {\n\t\treturn zap.Bool(name, true)\n\t}\n\treturn zap.Skip()\n}\n"
  },
  {
    "path": "fxevent/zap_test.go",
    "content": "// Copyright (c) 2021 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage fxevent\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"os\"\n\t\"strings\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\t\"go.uber.org/zap\"\n\t\"go.uber.org/zap/zapcore\"\n\t\"go.uber.org/zap/zaptest/observer\"\n)\n\nfunc TestZapLogger(t *testing.T) {\n\tt.Parallel()\n\n\tsomeError := errors.New(\"some error\")\n\n\ttests := []struct {\n\t\tname        string\n\t\tgive        Event\n\t\twantMessage string\n\t\twantFields  map[string]any\n\t}{\n\t\t{\n\t\t\tname: \"OnStartExecuting\",\n\t\t\tgive: &OnStartExecuting{\n\t\t\t\tFunctionName: \"hook.onStart\",\n\t\t\t\tCallerName:   \"bytes.NewBuffer\",\n\t\t\t},\n\t\t\twantMessage: \"OnStart hook executing\",\n\t\t\twantFields: map[string]any{\n\t\t\t\t\"caller\": \"bytes.NewBuffer\",\n\t\t\t\t\"callee\": \"hook.onStart\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"OnStopExecuting\",\n\t\t\tgive: &OnStopExecuting{\n\t\t\t\tFunctionName: \"hook.onStop1\",\n\t\t\t\tCallerName:   \"bytes.NewBuffer\",\n\t\t\t},\n\t\t\twantMessage: \"OnStop hook executing\",\n\t\t\twantFields: map[string]any{\n\t\t\t\t\"caller\": \"bytes.NewBuffer\",\n\t\t\t\t\"callee\": \"hook.onStop1\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"OnStopExecuted/Error\",\n\t\t\tgive: &OnStopExecuted{\n\t\t\t\tFunctionName: \"hook.onStart1\",\n\t\t\t\tCallerName:   \"bytes.NewBuffer\",\n\t\t\t\tErr:          fmt.Errorf(\"some error\"),\n\t\t\t},\n\t\t\twantMessage: \"OnStop hook failed\",\n\t\t\twantFields: map[string]any{\n\t\t\t\t\"caller\": \"bytes.NewBuffer\",\n\t\t\t\t\"callee\": \"hook.onStart1\",\n\t\t\t\t\"error\":  \"some error\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"OnStopExecuted\",\n\t\t\tgive: &OnStopExecuted{\n\t\t\t\tFunctionName: \"hook.onStart1\",\n\t\t\t\tCallerName:   \"bytes.NewBuffer\",\n\t\t\t\tRuntime:      time.Millisecond * 3,\n\t\t\t},\n\t\t\twantMessage: \"OnStop hook executed\",\n\t\t\twantFields: map[string]any{\n\t\t\t\t\"caller\":  \"bytes.NewBuffer\",\n\t\t\t\t\"callee\":  \"hook.onStart1\",\n\t\t\t\t\"runtime\": \"3ms\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"OnStartExecuted/Error\",\n\t\t\tgive: &OnStartExecuted{\n\t\t\t\tFunctionName: \"hook.onStart1\",\n\t\t\t\tCallerName:   \"bytes.NewBuffer\",\n\t\t\t\tErr:          fmt.Errorf(\"some error\"),\n\t\t\t},\n\t\t\twantMessage: \"OnStart hook failed\",\n\t\t\twantFields: map[string]any{\n\t\t\t\t\"caller\": \"bytes.NewBuffer\",\n\t\t\t\t\"callee\": \"hook.onStart1\",\n\t\t\t\t\"error\":  \"some error\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"OnStartExecuted\",\n\t\t\tgive: &OnStartExecuted{\n\t\t\t\tFunctionName: \"hook.onStart1\",\n\t\t\t\tCallerName:   \"bytes.NewBuffer\",\n\t\t\t\tRuntime:      time.Millisecond * 3,\n\t\t\t},\n\t\t\twantMessage: \"OnStart hook executed\",\n\t\t\twantFields: map[string]any{\n\t\t\t\t\"caller\":  \"bytes.NewBuffer\",\n\t\t\t\t\"callee\":  \"hook.onStart1\",\n\t\t\t\t\"runtime\": \"3ms\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"Supplied\",\n\t\t\tgive: &Supplied{\n\t\t\t\tTypeName:    \"*bytes.Buffer\",\n\t\t\t\tStackTrace:  []string{\"main.main\", \"runtime.main\"},\n\t\t\t\tModuleTrace: []string{\"main.main\"},\n\t\t\t},\n\t\t\twantMessage: \"supplied\",\n\t\t\twantFields: map[string]any{\n\t\t\t\t\"type\":        \"*bytes.Buffer\",\n\t\t\t\t\"stacktrace\":  []any{\"main.main\", \"runtime.main\"},\n\t\t\t\t\"moduletrace\": []any{\"main.main\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"Supplied/Error\",\n\t\t\tgive: &Supplied{\n\t\t\t\tTypeName:    \"*bytes.Buffer\",\n\t\t\t\tStackTrace:  []string{\"main.main\", \"runtime.main\"},\n\t\t\t\tModuleTrace: []string{\"main.main\"},\n\t\t\t\tErr:         someError,\n\t\t\t},\n\t\t\twantMessage: \"error encountered while applying options\",\n\t\t\twantFields: map[string]any{\n\t\t\t\t\"type\":        \"*bytes.Buffer\",\n\t\t\t\t\"stacktrace\":  []any{\"main.main\", \"runtime.main\"},\n\t\t\t\t\"moduletrace\": []any{\"main.main\"},\n\t\t\t\t\"error\":       \"some error\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"Provide\",\n\t\t\tgive: &Provided{\n\t\t\t\tConstructorName: \"bytes.NewBuffer()\",\n\t\t\t\tStackTrace:      []string{\"main.main\", \"runtime.main\"},\n\t\t\t\tModuleTrace:     []string{\"main.main\"},\n\t\t\t\tModuleName:      \"myModule\",\n\t\t\t\tOutputTypeNames: []string{\"*bytes.Buffer\"},\n\t\t\t\tPrivate:         false,\n\t\t\t},\n\t\t\twantMessage: \"provided\",\n\t\t\twantFields: map[string]any{\n\t\t\t\t\"constructor\": \"bytes.NewBuffer()\",\n\t\t\t\t\"stacktrace\":  []any{\"main.main\", \"runtime.main\"},\n\t\t\t\t\"moduletrace\": []any{\"main.main\"},\n\t\t\t\t\"type\":        \"*bytes.Buffer\",\n\t\t\t\t\"module\":      \"myModule\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"PrivateProvide\",\n\t\t\tgive: &Provided{\n\t\t\t\tConstructorName: \"bytes.NewBuffer()\",\n\t\t\t\tStackTrace:      []string{\"main.main\", \"runtime.main\"},\n\t\t\t\tModuleTrace:     []string{\"main.main\"},\n\t\t\t\tModuleName:      \"myModule\",\n\t\t\t\tOutputTypeNames: []string{\"*bytes.Buffer\"},\n\t\t\t\tPrivate:         true,\n\t\t\t},\n\t\t\twantMessage: \"provided\",\n\t\t\twantFields: map[string]any{\n\t\t\t\t\"constructor\": \"bytes.NewBuffer()\",\n\t\t\t\t\"stacktrace\":  []any{\"main.main\", \"runtime.main\"},\n\t\t\t\t\"moduletrace\": []any{\"main.main\"},\n\t\t\t\t\"type\":        \"*bytes.Buffer\",\n\t\t\t\t\"module\":      \"myModule\",\n\t\t\t\t\"private\":     true,\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"Provide/Error\",\n\t\t\tgive: &Provided{\n\t\t\t\tStackTrace:  []string{\"main.main\", \"runtime.main\"},\n\t\t\t\tModuleTrace: []string{\"main.main\"},\n\t\t\t\tErr:         someError,\n\t\t\t},\n\t\t\twantMessage: \"error encountered while applying options\",\n\t\t\twantFields: map[string]any{\n\t\t\t\t\"stacktrace\":  []any{\"main.main\", \"runtime.main\"},\n\t\t\t\t\"moduletrace\": []any{\"main.main\"},\n\t\t\t\t\"error\":       \"some error\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"Replace\",\n\t\t\tgive: &Replaced{\n\t\t\t\tModuleName:      \"myModule\",\n\t\t\t\tStackTrace:      []string{\"main.main\", \"runtime.main\"},\n\t\t\t\tModuleTrace:     []string{\"main.main\"},\n\t\t\t\tOutputTypeNames: []string{\"*bytes.Buffer\"},\n\t\t\t},\n\t\t\twantMessage: \"replaced\",\n\t\t\twantFields: map[string]any{\n\t\t\t\t\"type\":        \"*bytes.Buffer\",\n\t\t\t\t\"stacktrace\":  []any{\"main.main\", \"runtime.main\"},\n\t\t\t\t\"moduletrace\": []any{\"main.main\"},\n\t\t\t\t\"module\":      \"myModule\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"Replace/Error\",\n\t\t\tgive: &Replaced{\n\t\t\t\tStackTrace:  []string{\"main.main\", \"runtime.main\"},\n\t\t\t\tModuleTrace: []string{\"main.main\"},\n\t\t\t\tErr:         someError,\n\t\t\t},\n\n\t\t\twantMessage: \"error encountered while replacing\",\n\t\t\twantFields: map[string]any{\n\t\t\t\t\"stacktrace\":  []any{\"main.main\", \"runtime.main\"},\n\t\t\t\t\"moduletrace\": []any{\"main.main\"},\n\t\t\t\t\"error\":       \"some error\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"Decorate\",\n\t\t\tgive: &Decorated{\n\t\t\t\tDecoratorName:   \"bytes.NewBuffer()\",\n\t\t\t\tStackTrace:      []string{\"main.main\", \"runtime.main\"},\n\t\t\t\tModuleTrace:     []string{\"main.main\"},\n\t\t\t\tModuleName:      \"myModule\",\n\t\t\t\tOutputTypeNames: []string{\"*bytes.Buffer\"},\n\t\t\t},\n\t\t\twantMessage: \"decorated\",\n\t\t\twantFields: map[string]any{\n\t\t\t\t\"decorator\":   \"bytes.NewBuffer()\",\n\t\t\t\t\"stacktrace\":  []any{\"main.main\", \"runtime.main\"},\n\t\t\t\t\"moduletrace\": []any{\"main.main\"},\n\t\t\t\t\"type\":        \"*bytes.Buffer\",\n\t\t\t\t\"module\":      \"myModule\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"Decorate/Error\",\n\t\t\tgive: &Decorated{\n\t\t\t\tStackTrace:  []string{\"main.main\", \"runtime.main\"},\n\t\t\t\tModuleTrace: []string{\"main.main\"},\n\t\t\t\tErr:         someError,\n\t\t\t},\n\t\t\twantMessage: \"error encountered while applying options\",\n\t\t\twantFields: map[string]any{\n\t\t\t\t\"stacktrace\":  []any{\"main.main\", \"runtime.main\"},\n\t\t\t\t\"moduletrace\": []any{\"main.main\"},\n\t\t\t\t\"error\":       \"some error\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:        \"Run\",\n\t\t\tgive:        &Run{Name: \"bytes.NewBuffer()\", Kind: \"constructor\", Runtime: time.Second},\n\t\t\twantMessage: \"run\",\n\t\t\twantFields: map[string]any{\n\t\t\t\t\"name\":    \"bytes.NewBuffer()\",\n\t\t\t\t\"kind\":    \"constructor\",\n\t\t\t\t\"runtime\": \"1s\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"Run with module\",\n\t\t\tgive: &Run{\n\t\t\t\tName:       \"bytes.NewBuffer()\",\n\t\t\t\tKind:       \"constructor\",\n\t\t\t\tModuleName: \"myModule\",\n\t\t\t\tRuntime:    time.Millisecond,\n\t\t\t},\n\t\t\twantMessage: \"run\",\n\t\t\twantFields: map[string]any{\n\t\t\t\t\"name\":    \"bytes.NewBuffer()\",\n\t\t\t\t\"kind\":    \"constructor\",\n\t\t\t\t\"module\":  \"myModule\",\n\t\t\t\t\"runtime\": \"1ms\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"Run/Error\",\n\t\t\tgive: &Run{\n\t\t\t\tName: \"bytes.NewBuffer()\",\n\t\t\t\tKind: \"constructor\",\n\t\t\t\tErr:  someError,\n\t\t\t},\n\t\t\twantMessage: \"error returned\",\n\t\t\twantFields: map[string]any{\n\t\t\t\t\"name\":  \"bytes.NewBuffer()\",\n\t\t\t\t\"kind\":  \"constructor\",\n\t\t\t\t\"error\": \"some error\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:        \"Invoking/Success\",\n\t\t\tgive:        &Invoking{ModuleName: \"myModule\", FunctionName: \"bytes.NewBuffer()\"},\n\t\t\twantMessage: \"invoking\",\n\t\t\twantFields: map[string]any{\n\t\t\t\t\"function\": \"bytes.NewBuffer()\",\n\t\t\t\t\"module\":   \"myModule\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:        \"Invoked/Error\",\n\t\t\tgive:        &Invoked{FunctionName: \"bytes.NewBuffer()\", Err: someError},\n\t\t\twantMessage: \"invoke failed\",\n\t\t\twantFields: map[string]any{\n\t\t\t\t\"error\":    \"some error\",\n\t\t\t\t\"stack\":    \"\",\n\t\t\t\t\"function\": \"bytes.NewBuffer()\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:        \"Start/Error\",\n\t\t\tgive:        &Started{Err: someError},\n\t\t\twantMessage: \"start failed\",\n\t\t\twantFields: map[string]any{\n\t\t\t\t\"error\": \"some error\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:        \"Stopping\",\n\t\t\tgive:        &Stopping{Signal: os.Interrupt},\n\t\t\twantMessage: \"received signal\",\n\t\t\twantFields: map[string]any{\n\t\t\t\t\"signal\": \"INTERRUPT\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:        \"Stopped/Error\",\n\t\t\tgive:        &Stopped{Err: someError},\n\t\t\twantMessage: \"stop failed\",\n\t\t\twantFields: map[string]any{\n\t\t\t\t\"error\": \"some error\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:        \"RollingBack/Error\",\n\t\t\tgive:        &RollingBack{StartErr: someError},\n\t\t\twantMessage: \"start failed, rolling back\",\n\t\t\twantFields: map[string]any{\n\t\t\t\t\"error\": \"some error\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:        \"RolledBack/Error\",\n\t\t\tgive:        &RolledBack{Err: someError},\n\t\t\twantMessage: \"rollback failed\",\n\t\t\twantFields: map[string]any{\n\t\t\t\t\"error\": \"some error\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:        \"Started\",\n\t\t\tgive:        &Started{},\n\t\t\twantMessage: \"started\",\n\t\t\twantFields:  map[string]any{},\n\t\t},\n\t\t{\n\t\t\tname:        \"LoggerInitialized/Error\",\n\t\t\tgive:        &LoggerInitialized{Err: someError},\n\t\t\twantMessage: \"custom logger initialization failed\",\n\t\t\twantFields: map[string]any{\n\t\t\t\t\"error\": \"some error\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:        \"LoggerInitialized\",\n\t\t\tgive:        &LoggerInitialized{ConstructorName: \"bytes.NewBuffer()\"},\n\t\t\twantMessage: \"initialized custom fxevent.Logger\",\n\t\t\twantFields: map[string]any{\n\t\t\t\t\"function\": \"bytes.NewBuffer()\",\n\t\t\t},\n\t\t},\n\t}\n\n\tt.Run(\"debug observer, log at default (info)\", func(t *testing.T) {\n\t\tfor _, tt := range tests {\n\t\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\t\tt.Parallel()\n\n\t\t\t\tcore, observedLogs := observer.New(zap.DebugLevel)\n\t\t\t\t(&ZapLogger{Logger: zap.New(core)}).LogEvent(tt.give)\n\n\t\t\t\tlogs := observedLogs.TakeAll()\n\t\t\t\trequire.Len(t, logs, 1)\n\t\t\t\tgot := logs[0]\n\n\t\t\t\tassert.Equal(t, tt.wantMessage, got.Message)\n\t\t\t\tassert.Equal(t, tt.wantFields, got.ContextMap())\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"info observer, log at debug\", func(t *testing.T) {\n\t\tfor _, tt := range tests {\n\t\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\t\tt.Parallel()\n\n\t\t\t\tcore, observedLogs := observer.New(zap.InfoLevel)\n\t\t\t\tl := &ZapLogger{Logger: zap.New(core)}\n\t\t\t\tl.UseLogLevel(zapcore.DebugLevel)\n\t\t\t\tl.LogEvent(tt.give)\n\n\t\t\t\tlogs := observedLogs.TakeAll()\n\t\t\t\t// logs are not visible unless they are errors\n\t\t\t\tif strings.HasSuffix(tt.name, \"/Error\") {\n\t\t\t\t\trequire.Len(t, logs, 1)\n\t\t\t\t\tgot := logs[0]\n\t\t\t\t\tassert.Equal(t, tt.wantMessage, got.Message)\n\t\t\t\t\tassert.Equal(t, tt.wantFields, got.ContextMap())\n\t\t\t\t} else {\n\t\t\t\t\trequire.Len(t, logs, 0)\n\t\t\t\t}\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"info observer, log/error at debug\", func(t *testing.T) {\n\t\tfor _, tt := range tests {\n\t\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\t\tt.Parallel()\n\n\t\t\t\tcore, observedLogs := observer.New(zap.InfoLevel)\n\t\t\t\tl := &ZapLogger{Logger: zap.New(core)}\n\t\t\t\tl.UseLogLevel(zapcore.DebugLevel)\n\t\t\t\tl.UseErrorLevel(zapcore.DebugLevel)\n\t\t\t\tl.LogEvent(tt.give)\n\n\t\t\t\tlogs := observedLogs.TakeAll()\n\t\t\t\trequire.Len(t, logs, 0, \"no logs should be visible\")\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"test setting log levels\", func(t *testing.T) {\n\t\tlevels := []zapcore.Level{\n\t\t\tzapcore.DebugLevel,\n\t\t\tzapcore.WarnLevel,\n\t\t\tzapcore.DPanicLevel,\n\t\t\tzapcore.PanicLevel,\n\t\t}\n\n\t\tfor _, level := range levels {\n\t\t\tcore, observedLogs := observer.New(level)\n\t\t\tlogger := &ZapLogger{Logger: zap.New(core)}\n\t\t\tlogger.UseLogLevel(level)\n\t\t\tfunc() {\n\t\t\t\tdefer func() {\n\t\t\t\t\trecover()\n\t\t\t\t}()\n\t\t\t\tlogger.LogEvent(&OnStartExecuting{\n\t\t\t\t\tFunctionName: \"hook.onStart\",\n\t\t\t\t\tCallerName:   \"bytes.NewBuffer\",\n\t\t\t\t})\n\t\t\t}()\n\t\t\tlogs := observedLogs.TakeAll()\n\t\t\trequire.Len(t, logs, 1)\n\t\t}\n\t})\n\n\tt.Run(\"test setting error log levels\", func(t *testing.T) {\n\t\tlevels := []zapcore.Level{\n\t\t\tzapcore.DebugLevel,\n\t\t\tzapcore.WarnLevel,\n\t\t\tzapcore.DPanicLevel,\n\t\t\tzapcore.PanicLevel,\n\t\t\tzapcore.FatalLevel,\n\t\t}\n\n\t\tfor _, level := range levels {\n\t\t\tcore, observedLogs := observer.New(level)\n\t\t\tlogger := &ZapLogger{Logger: zap.New(core, zap.WithFatalHook(zapcore.WriteThenPanic))}\n\t\t\tlogger.UseErrorLevel(level)\n\t\t\tfunc() {\n\t\t\t\tdefer func() {\n\t\t\t\t\trecover()\n\t\t\t\t}()\n\t\t\t\tlogger.LogEvent(&OnStopExecuted{\n\t\t\t\t\tFunctionName: \"hook.onStart1\",\n\t\t\t\t\tCallerName:   \"bytes.NewBuffer\",\n\t\t\t\t\tErr:          fmt.Errorf(\"some error\"),\n\t\t\t\t})\n\t\t\t}()\n\t\t\tlogs := observedLogs.TakeAll()\n\t\t\trequire.Len(t, logs, 1)\n\t\t}\n\t})\n}\n"
  },
  {
    "path": "fxtest/app.go",
    "content": "// Copyright (c) 2019-2021 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage fxtest\n\nimport (\n\t\"context\"\n\n\t\"go.uber.org/fx\"\n)\n\n// App is a wrapper around fx.App that provides some testing helpers. By\n// default, it uses the provided TB as the application's logging backend.\ntype App struct {\n\t*fx.App\n\n\ttb TB\n}\n\n// New creates a new test application.\nfunc New(tb TB, opts ...fx.Option) *App {\n\tallOpts := make([]fx.Option, 0, len(opts)+1)\n\tallOpts = append(allOpts, WithTestLogger(tb))\n\tallOpts = append(allOpts, opts...)\n\n\tapp := fx.New(allOpts...)\n\tif err := app.Err(); err != nil {\n\t\ttb.Errorf(\"fx.New failed: %v\", err)\n\t\ttb.FailNow()\n\t}\n\n\treturn &App{\n\t\tApp: app,\n\t\ttb:  tb,\n\t}\n}\n\n// RequireStart calls Start, failing the test if an error is encountered.\nfunc (app *App) RequireStart() *App {\n\tstartCtx, cancel := context.WithTimeout(context.Background(), app.StartTimeout())\n\tdefer cancel()\n\n\tif err := app.Start(startCtx); err != nil {\n\t\tapp.tb.Errorf(\"application didn't start cleanly: %v\", err)\n\t\tapp.tb.FailNow()\n\t}\n\treturn app\n}\n\n// RequireStop calls Stop, failing the test if an error is encountered.\nfunc (app *App) RequireStop() {\n\tstopCtx, cancel := context.WithTimeout(context.Background(), app.StopTimeout())\n\tdefer cancel()\n\n\tif err := app.Stop(stopCtx); err != nil {\n\t\tapp.tb.Errorf(\"application didn't stop cleanly: %v\", err)\n\t\tapp.tb.FailNow()\n\t}\n}\n"
  },
  {
    "path": "fxtest/app_test.go",
    "content": "// Copyright (c) 2019-2021 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage fxtest\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"go.uber.org/fx\"\n)\n\nfunc TestApp(t *testing.T) {\n\tt.Parallel()\n\n\tt.Run(\"Success\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tspy := newTB()\n\n\t\tNew(spy).RequireStart().RequireStop()\n\n\t\tassert.Zero(t, spy.failures, \"App didn't start and stop cleanly.\")\n\t\tassert.Contains(t, spy.logs.String(), \"[Fx] RUNNING\", \"Expected to write logs to TB.\")\n\t})\n\n\tt.Run(\"NewFailure\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tspy := newTB()\n\n\t\tNew(\n\t\t\tspy,\n\n\t\t\t// Missing string dependency in container.\n\t\t\tfx.Invoke(func(string) {}),\n\t\t)\n\n\t\tassert.Equal(t, 1, spy.failures, \"Expected app to error on New.\")\n\t\tassert.Contains(t, spy.errors.String(), \"New failed\", \"Expected to write error to TB\")\n\t})\n\n\tt.Run(\"StartError\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tspy := newTB()\n\n\t\tNew(\n\t\t\tspy,\n\n\t\t\tfx.Invoke(func(lc fx.Lifecycle) {\n\t\t\t\tlc.Append(fx.Hook{\n\t\t\t\t\tOnStart: func(context.Context) error { return errors.New(\"fail\") },\n\t\t\t\t})\n\t\t\t}),\n\t\t).RequireStart()\n\n\t\tassert.Equal(t, 1, spy.failures, \"Expected app to error on start.\")\n\t\tassert.Contains(t, spy.errors.String(), \"didn't start cleanly\", \"Expected to write errors to TB.\")\n\t})\n\n\tt.Run(\"StopFailure\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tspy := newTB()\n\n\t\tconstruct := func(lc fx.Lifecycle) struct{} {\n\t\t\tlc.Append(fx.Hook{OnStop: func(context.Context) error { return errors.New(\"fail\") }})\n\t\t\treturn struct{}{}\n\t\t}\n\t\tNew(\n\t\t\tspy,\n\t\t\tfx.Provide(construct),\n\t\t\tfx.Invoke(func(struct{}) {}),\n\t\t).RequireStart().RequireStop()\n\n\t\tassert.Equal(t, 1, spy.failures, \"Expected Stop to fail.\")\n\t\tassert.Contains(t, spy.errors.String(), \"didn't stop cleanly\", \"Expected to write errors to TB.\")\n\t})\n}\n"
  },
  {
    "path": "fxtest/doc.go",
    "content": "// Copyright (c) 2024 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\n// Package fxtest provides utilities for testing Fx modules,\n// and code that directly uses Fx.\npackage fxtest\n"
  },
  {
    "path": "fxtest/lifecycle.go",
    "content": "// Copyright (c) 2020-2021 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage fxtest\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"io\"\n\t\"os\"\n\n\t\"go.uber.org/fx\"\n\t\"go.uber.org/fx/internal/fxclock\"\n\t\"go.uber.org/fx/internal/fxlog\"\n\t\"go.uber.org/fx/internal/lifecycle\"\n\t\"go.uber.org/fx/internal/testutil\"\n)\n\n// If a testing.T is unspecified, degrade to printing to stderr to provide\n// meaningful messages.\ntype panicT struct {\n\tW io.Writer // stream to which we'll write messages\n\n\t// lastError message written to the stream with Errorf. We'll use this\n\t// as the panic message if FailNow is called.\n\tlastErr string\n}\n\nvar _ TB = &panicT{}\n\nfunc (t *panicT) format(s string, args ...any) string {\n\treturn fmt.Sprintf(s, args...)\n}\n\nfunc (t *panicT) Logf(s string, args ...any) {\n\tfmt.Fprintln(t.W, t.format(s, args...))\n}\n\nfunc (t *panicT) Errorf(s string, args ...any) {\n\tt.lastErr = t.format(s, args...)\n\tfmt.Fprintln(t.W, t.lastErr)\n}\n\nfunc (t *panicT) FailNow() {\n\tif len(t.lastErr) > 0 {\n\t\tpanic(t.lastErr)\n\t}\n\n\tpanic(\"test lifecycle failed\")\n}\n\n// LifecycleOption modifies the behavior of the [Lifecycle]\n// when passed to [NewLifecycle].\ntype LifecycleOption interface {\n\tapply(*Lifecycle)\n}\n\n// EnforceTimeout will cause the [Lifecycle]'s Start and Stop methods\n// to return an error as soon as context expires,\n// regardless of whether specific hooks respect the timeout.\nfunc EnforceTimeout(enforce bool) LifecycleOption {\n\treturn &enforceTimeout{\n\t\tenforce: enforce,\n\t}\n}\n\ntype enforceTimeout struct {\n\tenforce bool\n}\n\nfunc (e *enforceTimeout) apply(lc *Lifecycle) {\n\tlc.enforceTimeout = e.enforce\n}\n\nvar _ LifecycleOption = (*enforceTimeout)(nil)\n\n// Lifecycle is a testing spy for fx.Lifecycle. It exposes Start and Stop\n// methods (and some test-specific helpers) so that unit tests can exercise\n// hooks.\ntype Lifecycle struct {\n\tt  TB\n\tlc *lifecycle.Lifecycle\n\n\tenforceTimeout bool\n}\n\nvar _ fx.Lifecycle = (*Lifecycle)(nil)\n\n// NewLifecycle creates a new test lifecycle.\nfunc NewLifecycle(t TB, opts ...LifecycleOption) *Lifecycle {\n\tvar w io.Writer\n\tif t != nil {\n\t\tw = testutil.WriteSyncer{T: t}\n\t} else {\n\t\tw = os.Stderr\n\t\tt = &panicT{W: os.Stderr}\n\t}\n\tlc := &Lifecycle{\n\t\tlc: lifecycle.New(fxlog.DefaultLogger(w), fxclock.System),\n\t\tt:  t,\n\t}\n\tfor _, opt := range opts {\n\t\topt.apply(lc)\n\t}\n\treturn lc\n}\n\nfunc (l *Lifecycle) withTimeout(ctx context.Context, fn func(context.Context) error) error {\n\tif !l.enforceTimeout {\n\t\treturn fn(ctx)\n\t}\n\n\t// Cancel on timeout in case function only respects\n\t// cancellation and not deadline exceeded.\n\tctx, cancel := context.WithCancel(ctx)\n\tdefer cancel()\n\n\tc := make(chan error, 1) // buffered to avoid goroutine leak\n\tgo func() {\n\t\tc <- fn(ctx)\n\t}()\n\n\tvar err error\n\tselect {\n\tcase err = <-c:\n\tcase <-ctx.Done():\n\t\terr = ctx.Err()\n\t}\n\treturn err\n}\n\n// Start executes all registered OnStart hooks in order, halting at the first\n// hook that doesn't succeed.\nfunc (l *Lifecycle) Start(ctx context.Context) error {\n\treturn l.withTimeout(ctx, l.lc.Start)\n}\n\n// RequireStart calls Start with context.Background(), failing the test if an\n// error is encountered.\nfunc (l *Lifecycle) RequireStart() *Lifecycle {\n\tctx, cancel := context.WithCancel(context.Background())\n\tdefer cancel()\n\n\tif err := l.Start(ctx); err != nil {\n\t\tl.t.Errorf(\"lifecycle didn't start cleanly: %v\", err)\n\t\tl.t.FailNow()\n\t}\n\treturn l\n}\n\n// Stop calls all OnStop hooks whose OnStart counterpart was called, running\n// in reverse order.\n//\n// If any hook returns an error, execution continues for a best-effort\n// cleanup. Any errors encountered are collected into a single error and\n// returned.\nfunc (l *Lifecycle) Stop(ctx context.Context) error {\n\treturn l.withTimeout(ctx, l.lc.Stop)\n}\n\n// RequireStop calls Stop with context.Background(), failing the test if an error\n// is encountered.\nfunc (l *Lifecycle) RequireStop() {\n\tctx, cancel := context.WithCancel(context.Background())\n\tdefer cancel()\n\n\tif err := l.Stop(ctx); err != nil {\n\t\tl.t.Errorf(\"lifecycle didn't stop cleanly: %v\", err)\n\t\tl.t.FailNow()\n\t}\n}\n\n// Append registers a new Hook.\nfunc (l *Lifecycle) Append(h fx.Hook) {\n\tl.lc.Append(lifecycle.Hook{\n\t\tOnStart: h.OnStart,\n\t\tOnStop:  h.OnStop,\n\t})\n}\n"
  },
  {
    "path": "fxtest/lifecycle_test.go",
    "content": "// Copyright (c) 2020 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage fxtest\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\t\"go.uber.org/fx\"\n\t\"go.uber.org/goleak\"\n)\n\nfunc TestLifecycle(t *testing.T) {\n\tt.Parallel()\n\n\tt.Run(\"Success\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tspy := newTB()\n\n\t\tn := 0\n\t\tlc := NewLifecycle(spy)\n\t\tlc.Append(fx.Hook{\n\t\t\tOnStart: func(context.Context) error { n++; return nil },\n\t\t\tOnStop:  func(context.Context) error { n++; return nil },\n\t\t})\n\t\tlc.RequireStart().RequireStop()\n\n\t\tassert.Zero(t, spy.failures, \"Lifecycle start/stop failed.\")\n\t\tassert.Equal(t, 2, n, \"Didn't run start and stop hooks.\")\n\t})\n\n\tt.Run(\"StartError\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tspy := newTB()\n\t\tlc := NewLifecycle(spy)\n\t\tlc.Append(fx.Hook{OnStart: func(context.Context) error { return errors.New(\"fail\") }})\n\n\t\tlc.RequireStart()\n\t\tassert.Equal(t, 1, spy.failures, \"Expected lifecycle start to fail.\")\n\n\t\tlc.RequireStop()\n\t\tassert.Equal(t, 1, spy.failures, \"Expected lifecycle stop to succeed.\")\n\t})\n\n\tt.Run(\"StopFailure\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tspy := newTB()\n\t\tlc := NewLifecycle(spy)\n\t\tlc.Append(fx.Hook{OnStop: func(context.Context) error { return errors.New(\"fail\") }})\n\n\t\tlc.RequireStart()\n\t\tassert.Equal(t, 0, spy.failures, \"Expected lifecycle start to succeed.\")\n\n\t\tlc.RequireStop()\n\t\tassert.Equal(t, 1, spy.failures, \"Expected lifecycle stop to fail.\")\n\t})\n\n\tt.Run(\"RequireLeakDetection\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tspy := newTB()\n\t\tlc := NewLifecycle(spy)\n\n\t\tstop := make(chan struct{})\n\t\tstopped := make(chan struct{})\n\n\t\tonStart := func(ctx context.Context) error {\n\t\t\tgo func() {\n\t\t\t\t<-stop\n\t\t\t\tclose(stopped)\n\t\t\t}()\n\t\t\treturn ctx.Err()\n\t\t}\n\n\t\tonStop := func(ctx context.Context) error {\n\t\t\tclose(stop)\n\t\t\t<-stopped\n\t\t\treturn ctx.Err()\n\t\t}\n\n\t\tlc.Append(fx.Hook{\n\t\t\tOnStart: onStart,\n\t\t\tOnStop:  onStop,\n\t\t})\n\n\t\tlc.RequireStart()\n\t\tassert.Equal(t, 0, spy.failures, \"Expected lifecycle start to succeed.\")\n\n\t\tlc.RequireStop()\n\t\tassert.Equal(t, 0, spy.failures, \"Expected lifecycle to stop.\")\n\t})\n}\n\nfunc TestEnforceTimeout(t *testing.T) {\n\t// These tests directly call Start and Stop\n\t// rather than RequireStart and RequireStop\n\t// because EnforceTimeout does not apply to those.\n\n\tt.Run(\"StartHookTimeout\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\twait := make(chan struct{})\n\t\tdefer close(wait) // force timeout by blocking OnStart until end of test\n\n\t\tspy := newTB()\n\t\tlc := NewLifecycle(spy, EnforceTimeout(true))\n\t\tlc.Append(fx.Hook{\n\t\t\tOnStart: func(context.Context) error {\n\t\t\t\t<-wait\n\t\t\t\treturn nil\n\t\t\t},\n\t\t})\n\n\t\tctx, cancel := context.WithTimeout(context.Background(), 10*time.Millisecond)\n\t\tdefer cancel()\n\t\tassert.ErrorIs(t, lc.Start(ctx), context.DeadlineExceeded)\n\t\tassert.Zero(t, spy.failures)\n\t})\n\n\tt.Run(\"StopHookTimeout\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\twait := make(chan struct{})\n\t\tdefer close(wait) // force timeout by blocking OnStop until end of test\n\n\t\tspy := newTB()\n\t\tlc := NewLifecycle(spy, EnforceTimeout(true))\n\t\tlc.Append(fx.Hook{\n\t\t\tOnStop: func(context.Context) error {\n\t\t\t\t<-wait\n\t\t\t\treturn nil\n\t\t\t},\n\t\t})\n\n\t\trequire.NoError(t, lc.Start(context.Background()))\n\t\tctx, cancel := context.WithTimeout(context.Background(), 10*time.Millisecond)\n\t\tdefer cancel()\n\t\tassert.ErrorIs(t, lc.Stop(ctx), context.DeadlineExceeded)\n\t\tassert.Zero(t, spy.failures)\n\t})\n\n\tt.Run(\"NoTimeout\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tvar (\n\t\t\tstarted bool\n\t\t\tstopped bool\n\t\t)\n\n\t\tspy := newTB()\n\t\tlc := NewLifecycle(spy, EnforceTimeout(true))\n\t\tlc.Append(fx.Hook{\n\t\t\tOnStart: func(context.Context) error {\n\t\t\t\tstarted = true\n\t\t\t\treturn nil\n\t\t\t},\n\t\t\tOnStop: func(context.Context) error {\n\t\t\t\tstopped = true\n\t\t\t\treturn nil\n\t\t\t},\n\t\t})\n\n\t\tctx, cancel := context.WithTimeout(context.Background(), time.Hour)\n\t\tdefer cancel()\n\t\trequire.NoError(t, lc.Start(ctx))\n\t\trequire.NoError(t, lc.Stop(ctx))\n\t\tassert.True(t, started)\n\t\tassert.True(t, stopped)\n\t\tassert.Zero(t, spy.failures)\n\t})\n\n\tt.Run(\"OtherError\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tspy := newTB()\n\t\tlc := NewLifecycle(spy, EnforceTimeout(true))\n\t\tlc.Append(fx.Hook{\n\t\t\tOnStart: func(context.Context) error {\n\t\t\t\treturn errors.New(\"NOT a context-related error\")\n\t\t\t},\n\t\t})\n\n\t\tctx, cancel := context.WithTimeout(context.Background(), time.Hour)\n\t\tdefer cancel()\n\t\tassert.ErrorContains(t, lc.Start(ctx), \"NOT a context-related error\")\n\t\tassert.Zero(t, spy.failures)\n\t})\n}\n\nfunc TestLifecycle_OptionalT(t *testing.T) {\n\tt.Parallel()\n\n\tt.Run(\"success\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tlc := NewLifecycle(nil)\n\n\t\tvar started, stopped bool\n\t\tdefer func() {\n\t\t\tassert.True(t, started, \"not started\")\n\t\t\tassert.True(t, stopped, \"not stopped\")\n\t\t}()\n\t\tlc.Append(fx.Hook{\n\t\t\tOnStart: func(context.Context) error {\n\t\t\t\tassert.False(t, started, \"started twice\")\n\t\t\t\tstarted = true\n\t\t\t\treturn nil\n\t\t\t},\n\t\t\tOnStop: func(context.Context) error {\n\t\t\t\tassert.True(t, started, \"not yet started\")\n\t\t\t\tassert.False(t, stopped, \"stopped twice\")\n\t\t\t\tstopped = true\n\t\t\t\treturn nil\n\t\t\t},\n\t\t})\n\n\t\tlc.RequireStart().RequireStop()\n\t})\n\n\tt.Run(\"start error\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tlc := NewLifecycle(nil)\n\t\tlc.Append(fx.Hook{\n\t\t\tOnStart: func(context.Context) error {\n\t\t\t\treturn errors.New(\"great sadness\")\n\t\t\t},\n\t\t})\n\n\t\tvar pval any\n\t\tfunc() {\n\t\t\tdefer func() { pval = recover() }()\n\t\t\tlc.RequireStart()\n\t\t}()\n\t\trequire.NotNil(t, pval, \"must panic in case of failure\")\n\t\tassert.Contains(t, fmt.Sprint(pval), \"great sadness\")\n\t})\n}\n\nfunc TestPanicT(t *testing.T) {\n\tt.Parallel()\n\n\tt.Run(\"Logf\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tvar buff bytes.Buffer\n\t\tpt := panicT{W: &buff}\n\t\tpt.Logf(\"hello: %v\", \"world\")\n\n\t\tassert.Equal(t, \"hello: world\\n\", buff.String())\n\t})\n\n\tt.Run(\"Errorf\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tvar buff bytes.Buffer\n\t\tpt := panicT{W: &buff}\n\t\tpt.Errorf(\"hello: %v\", \"world\")\n\n\t\tassert.Equal(t, \"hello: world\\n\", buff.String())\n\n\t\t// Functionally there's no difference between Logf and Errorf\n\t\t// unless FailNow is called.\n\t\tt.Run(\"FailNow\", func(t *testing.T) {\n\t\t\tt.Parallel()\n\n\t\t\tvar pval any\n\t\t\tfunc() {\n\t\t\t\tdefer func() { pval = recover() }()\n\t\t\t\tpt.FailNow()\n\t\t\t}()\n\t\t\tassert.Equal(t, \"hello: world\", pval)\n\t\t})\n\t})\n\n\t// FailNow without calling Errorf will use the fixed message.\n\tt.Run(\"FailNow\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tvar buff bytes.Buffer\n\t\tpt := panicT{W: &buff}\n\t\tpt.Logf(\"hello: %v\", \"world\")\n\n\t\tvar pval any\n\t\tfunc() {\n\t\t\tdefer func() { pval = recover() }()\n\t\t\tpt.FailNow()\n\t\t}()\n\t\tassert.Equal(t, \"test lifecycle failed\", pval)\n\t})\n}\n\nfunc TestMain(m *testing.M) {\n\tgoleak.VerifyTestMain(m)\n}\n"
  },
  {
    "path": "fxtest/printer.go",
    "content": "// Copyright (c) 2019-2021 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage fxtest\n\nimport (\n\t\"go.uber.org/fx\"\n\t\"go.uber.org/fx/fxevent\"\n\t\"go.uber.org/fx/internal/fxlog\"\n\t\"go.uber.org/fx/internal/testutil\"\n)\n\n// NewTestLogger returns an fxlog.Logger that logs to the testing TB.\nfunc NewTestLogger(t TB) fxevent.Logger {\n\treturn fxlog.DefaultLogger(testutil.WriteSyncer{T: t})\n}\n\n// WithTestLogger returns an fx.Option that uses the provided TB\n// as the destination for Fx's log output.\nfunc WithTestLogger(t TB) fx.Option {\n\treturn fx.WithLogger(func() fxevent.Logger {\n\t\treturn NewTestLogger(t)\n\t})\n}\n\ntype testPrinter struct {\n\tTB\n}\n\n// NewTestPrinter returns a fx.Printer that logs to the testing TB.\nfunc NewTestPrinter(t TB) fx.Printer {\n\treturn &testPrinter{t}\n}\n\nfunc (p *testPrinter) Printf(format string, args ...any) {\n\tp.Logf(format, args...)\n}\n"
  },
  {
    "path": "fxtest/printer_test.go",
    "content": "// Copyright (c) 2019 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage fxtest\n\nimport (\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestNewTestPrinter(t *testing.T) {\n\tt.Parallel()\n\n\tspy := newTB()\n\tp := NewTestPrinter(spy)\n\tp.Printf(\"static\")\n\tp.Printf(\"dynamic %v\", 1)\n\tassert.Equal(t, `static\ndynamic 1\n`, spy.logs.String())\n}\n"
  },
  {
    "path": "fxtest/tb.go",
    "content": "// Copyright (c) 2020 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage fxtest\n\n// TB is a subset of the standard library's testing.TB interface. It's\n// satisfied by both *testing.T and *testing.B.\ntype TB interface {\n\tLogf(string, ...any)\n\tErrorf(string, ...any)\n\tFailNow()\n}\n"
  },
  {
    "path": "fxtest/tb_test.go",
    "content": "// Copyright (c) 2020 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage fxtest\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"testing\"\n)\n\n// Verify that TB always matches testing.T.\nvar _ TB = (*testing.T)(nil)\n\ntype tb struct {\n\tfailures int\n\terrors   *bytes.Buffer\n\tlogs     *bytes.Buffer\n}\n\nfunc newTB() *tb {\n\treturn &tb{0, &bytes.Buffer{}, &bytes.Buffer{}}\n}\n\nfunc (t *tb) FailNow() {\n\tt.failures++\n}\n\nfunc (t *tb) Errorf(format string, args ...any) {\n\tfmt.Fprintf(t.errors, format, args...)\n\tt.errors.WriteRune('\\n')\n}\n\nfunc (t *tb) Logf(format string, args ...any) {\n\tfmt.Fprintf(t.logs, format, args...)\n\tt.logs.WriteRune('\\n')\n}\n"
  },
  {
    "path": "go.mod",
    "content": "module go.uber.org/fx\n\ngo 1.24\n\nrequire (\n\tgithub.com/stretchr/testify v1.8.1\n\tgo.uber.org/dig v1.19.0\n\tgo.uber.org/goleak v1.2.0\n\tgo.uber.org/multierr v1.10.0\n\tgo.uber.org/zap v1.26.0\n\tgolang.org/x/sys v0.0.0-20220412211240-33da011f77ad\n)\n\nrequire (\n\tgithub.com/davecgh/go-spew v1.1.1 // indirect\n\tgithub.com/pmezard/go-difflib v1.0.0 // indirect\n\tgopkg.in/yaml.v3 v3.0.1 // indirect\n)\n"
  },
  {
    "path": "go.sum",
    "content": "github.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/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=\ngithub.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=\ngithub.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=\ngithub.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=\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/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 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=\ngithub.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=\ngo.uber.org/dig v1.19.0 h1:BACLhebsYdpQ7IROQ1AGPjrXcP5dF80U3gKoFzbaq/4=\ngo.uber.org/dig v1.19.0/go.mod h1:Us0rSJiThwCv2GteUN0Q7OKvU7n5J4dxZ9JKUXozFdE=\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.10.0 h1:S0h4aNzvfcFsC3dRF1jLoaov7oRaKqRGC/pUEJ2yvPQ=\ngo.uber.org/multierr v1.10.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/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/sys v0.0.0-20220412211240-33da011f77ad h1:ntjMns5wyP/fN65tdBD4g8J5w8n015+iIIs9rtjXkY0=\ngolang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/tools v0.1.5 h1:ouewzE6p+/VEB31YYnTbEJdi8pFqKp4P4n85vwo3DHA=\ngolang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=\ngopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=\ngopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/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": "inout.go",
    "content": "// Copyright (c) 2019 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage fx\n\nimport \"go.uber.org/dig\"\n\n// In can be embedded into a struct to mark it as a parameter struct.\n// This allows it to make use of advanced dependency injection features.\n// See package documentation for more information.\n//\n// It's recommended that shared modules use a single parameter struct to\n// provide a forward-compatible API:\n// adding new optional fields to a struct is backward-compatible,\n// so modules can evolve as needs change.\ntype In = dig.In\n\n// Out is the inverse of In: it marks a struct as a result struct so that\n// it can be used with advanced dependency injection features.\n// See package documentation for more information.\n//\n// It's recommended that shared modules use a single result struct to\n// provide a forward-compatible API:\n// adding new fields to a struct is backward-compatible,\n// so modules can produce more outputs as they grow.\ntype Out = dig.Out\n"
  },
  {
    "path": "inout_test.go",
    "content": "// Copyright (c) 2019 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage fx_test\n\nimport (\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"go.uber.org/dig\"\n\t\"go.uber.org/fx\"\n\t\"go.uber.org/fx/fxtest\"\n)\n\nfunc TestIn(t *testing.T) {\n\tt.Parallel()\n\n\ttype in struct {\n\t\tfx.In\n\t}\n\tassert.True(t, dig.IsIn(in{}), \"Expected dig.In to work with fx.In\")\n}\n\nfunc TestOut(t *testing.T) {\n\tt.Parallel()\n\n\ttype out struct {\n\t\tfx.Out\n\t}\n\tassert.True(t, dig.IsOut(out{}), \"expected dig.Out to work with fx.Out\")\n}\n\nfunc TestOptionalTypes(t *testing.T) {\n\tt.Parallel()\n\n\ttype foo struct{}\n\tnewFoo := func() *foo { return &foo{} }\n\n\ttype bar struct{}\n\tnewBar := func() *bar { return &bar{} }\n\n\ttype in struct {\n\t\tfx.In\n\n\t\tFoo *foo\n\t\tBar *bar `optional:\"true\"`\n\t}\n\n\tt.Run(\"NotProvided\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tran := false\n\t\tapp := fxtest.New(t, fx.Provide(newFoo), fx.Invoke(func(in in) {\n\t\t\tassert.NotNil(t, in.Foo, \"foo was not optional and provided, expected not nil\")\n\t\t\tassert.Nil(t, in.Bar, \"bar was optional and not provided, expected nil\")\n\t\t\tran = true\n\t\t}))\n\t\tapp.RequireStart().RequireStop()\n\t\tassert.True(t, ran, \"expected invoke to run\")\n\t})\n\n\tt.Run(\"Provided\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tran := false\n\t\tapp := fxtest.New(t, fx.Provide(newFoo, newBar), fx.Invoke(func(in in) {\n\t\t\tassert.NotNil(t, in.Foo, \"foo was not optional and provided, expected not nil\")\n\t\t\tassert.NotNil(t, in.Bar, \"bar was optional and provided, expected not nil\")\n\t\t\tran = true\n\t\t}))\n\t\tapp.RequireStart().RequireStop()\n\t\tassert.True(t, ran, \"expected invoke to run\")\n\t})\n}\n\nfunc TestNamedTypes(t *testing.T) {\n\tt.Parallel()\n\n\ttype a struct {\n\t\tname string\n\t}\n\n\t// a constructor that returns the type a with name \"foo\"\n\ttype fooOut struct {\n\t\tfx.Out\n\n\t\tA *a `name:\"foo\"`\n\t}\n\tnewFoo := func() fooOut {\n\t\treturn fooOut{\n\t\t\tA: &a{name: \"foo\"},\n\t\t}\n\t}\n\n\t// another constructor that returns the same type a with name \"bar\"\n\ttype barOut struct {\n\t\tfx.Out\n\n\t\tA *a `name:\"bar\"`\n\t}\n\tnewBar := func() barOut {\n\t\treturn barOut{\n\t\t\tA: &a{name: \"bar\"},\n\t\t}\n\t}\n\n\t// invoke with an fx.In that resolves both named types\n\ttype in struct {\n\t\tfx.In\n\n\t\tFoo *a `name:\"foo\"`\n\t\tBar *a `name:\"bar\"`\n\t}\n\tran := false\n\tapp := fxtest.New(t, fx.Provide(newFoo, newBar), fx.Invoke(func(in in) {\n\t\tassert.NotNil(t, in.Foo, \"expected in.Foo to be injected\")\n\t\tassert.Equal(t, \"foo\", in.Foo.name, \"expected to get type 'a' of name 'foo'\")\n\n\t\tassert.NotNil(t, in.Bar, \"expected in.Bar to be injected\")\n\t\tassert.Equal(t, \"bar\", in.Bar.name, \"expected to get a type 'a' of name 'bar'\")\n\n\t\tran = true\n\t}))\n\tapp.RequireStart().RequireStop()\n\tassert.True(t, ran, \"expected invoke to run\")\n}\n\nfunc TestIgnoreUnexported(t *testing.T) {\n\tt.Parallel()\n\n\ttype A struct{ ID int }\n\ttype B struct{ ID int }\n\n\ttype Params struct {\n\t\tfx.In `ignore-unexported:\"true\"`\n\n\t\tA A\n\t\tb B // will be ignored\n\t}\n\n\tran := false\n\trun := func(in Params) {\n\t\tdefer func() { ran = true }()\n\n\t\tassert.Equal(t, A{1}, in.A, \"A must be set\")\n\n\t\t// We provide a B to the container, but because the \"b\" field\n\t\t// is unexported, we don't expect it to be set.\n\t\tassert.Equal(t, B{0}, in.b, \"b must be unset\")\n\t}\n\tdefer func() {\n\t\tassert.True(t, ran, \"run was never called\")\n\t}()\n\n\tfxtest.New(t,\n\t\tfx.Supply(A{1}, B{2}),\n\t\tfx.Invoke(run),\n\t).RequireStart().RequireStop()\n}\n"
  },
  {
    "path": "internal/e2e/README.md",
    "content": "This directory holds end-to-end tests for Fx.\nEach subdirectory holds a complete Fx application\nand a test for it.\n\nThis is marked as a separate Go module to prevent this code from being bundled\nwith the Fx library and allow for dependencies that don't leak into Fx.\n"
  },
  {
    "path": "internal/e2e/go.mod",
    "content": "module go.uber.org/fx/internal/e2e\n\ngo 1.24\n\nrequire (\n\tgithub.com/stretchr/testify v1.8.2\n\tgo.uber.org/fx v1.19.2\n)\n\nrequire (\n\tgithub.com/davecgh/go-spew v1.1.1 // indirect\n\tgithub.com/pmezard/go-difflib v1.0.0 // indirect\n\tgo.uber.org/dig v1.19.0 // indirect\n\tgo.uber.org/multierr v1.10.0 // indirect\n\tgo.uber.org/zap v1.26.0 // indirect\n\tgolang.org/x/sys v0.0.0-20220412211240-33da011f77ad // indirect\n\tgopkg.in/yaml.v3 v3.0.1 // indirect\n)\n\nreplace go.uber.org/fx => ../..\n"
  },
  {
    "path": "internal/e2e/go.sum",
    "content": "github.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/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/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.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=\ngithub.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=\ngo.uber.org/dig v1.19.0 h1:BACLhebsYdpQ7IROQ1AGPjrXcP5dF80U3gKoFzbaq/4=\ngo.uber.org/dig v1.19.0/go.mod h1:Us0rSJiThwCv2GteUN0Q7OKvU7n5J4dxZ9JKUXozFdE=\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.10.0 h1:S0h4aNzvfcFsC3dRF1jLoaov7oRaKqRGC/pUEJ2yvPQ=\ngo.uber.org/multierr v1.10.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/sys v0.0.0-20220412211240-33da011f77ad h1:ntjMns5wyP/fN65tdBD4g8J5w8n015+iIIs9rtjXkY0=\ngolang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=\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": "internal/e2e/shutdowner_run_exitcode/main.go",
    "content": "// Copyright (c) 2023 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage main\n\nimport (\n\t\"go.uber.org/fx\"\n)\n\nfunc main() {\n\tfx.New(\n\t\tfx.Invoke(func(shutdowner fx.Shutdowner) error {\n\t\t\tshutdowner.Shutdown(fx.ExitCode(20))\n\t\t\treturn nil\n\t\t}),\n\t).Run()\n}\n"
  },
  {
    "path": "internal/e2e/shutdowner_run_exitcode/main_test.go",
    "content": "// Copyright (c) 2023 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage main\n\nimport (\n\t\"os\"\n\t\"os/exec\"\n\t\"path/filepath\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\t\"go.uber.org/fx/internal/testutil\"\n)\n\n// Hijacks the test binary so that the test can run main() as a subprocess\n// instead of trying to compile the program and run it directly.\nfunc TestMain(m *testing.M) {\n\t// If the test binary is named \"app\", then we're running as a subprocess.\n\t// Otherwise, run the tests.\n\tswitch filepath.Base(os.Args[0]) {\n\tcase \"app\":\n\t\tmain()\n\t\tos.Exit(0)\n\tdefault:\n\t\tos.Exit(m.Run())\n\t}\n}\n\n// Verifies that an Fx program running with Run\n// exits with the exit code passed to Shutdowner.\n//\n// Regression test for https://github.com/uber-go/fx/issues/1074.\nfunc TestShutdownExitCode(t *testing.T) {\n\texe, err := os.Executable()\n\trequire.NoError(t, err)\n\n\tout := testutil.WriteSyncer{T: t}\n\n\t// Run the test binary with the name 'app' so that it runs main().\n\tcmd := exec.Command(exe)\n\tcmd.Args[0] = \"app\"\n\tcmd.Stdout = &out\n\tcmd.Stderr = &out\n\n\t// The program should exit with code 20.\n\terr = cmd.Run()\n\trequire.Error(t, err)\n\n\tvar exitErr *exec.ExitError\n\trequire.ErrorAs(t, err, &exitErr)\n\n\tassert.Equal(t, 20, exitErr.ExitCode())\n}\n"
  },
  {
    "path": "internal/e2e/shutdowner_wait_exitcode/main.go",
    "content": "// Copyright (c) 2023 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage main\n\nimport (\n\t\"context\"\n\t\"log\"\n\t\"os\"\n\t\"time\"\n\n\t\"go.uber.org/fx\"\n)\n\nfunc main() {\n\tapp := fx.New(\n\t\tfx.Invoke(func(shutdowner fx.Shutdowner) error {\n\t\t\tshutdowner.Shutdown(fx.ExitCode(20))\n\t\t\treturn nil\n\t\t}),\n\t)\n\n\tctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)\n\tdefer cancel()\n\n\tif err := app.Start(ctx); err != nil {\n\t\tlog.Fatal(err)\n\t}\n\n\tsig := <-app.Wait()\n\n\tif err := app.Stop(ctx); err != nil {\n\t\tlog.Fatal(err)\n\t}\n\n\tos.Exit(sig.ExitCode)\n}\n"
  },
  {
    "path": "internal/e2e/shutdowner_wait_exitcode/main_test.go",
    "content": "// Copyright (c) 2023 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage main\n\nimport (\n\t\"os\"\n\t\"os/exec\"\n\t\"path/filepath\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\t\"go.uber.org/fx/internal/testutil\"\n)\n\n// Hijacks the test binary so that the test can run main() as a subprocess\n// instead of trying to compile the program and run it directly.\nfunc TestMain(m *testing.M) {\n\t// If the test binary is named \"app\", then we're running as a subprocess.\n\t// Otherwise, run the tests.\n\tswitch filepath.Base(os.Args[0]) {\n\tcase \"app\":\n\t\tmain()\n\t\tos.Exit(0)\n\tdefault:\n\t\tos.Exit(m.Run())\n\t}\n}\n\n// Verifies that an Fx program running with Run\n// exits with the exit code passed to Shutdowner.\n//\n// Regression test for https://github.com/uber-go/fx/issues/1074.\nfunc TestShutdownExitCode(t *testing.T) {\n\texe, err := os.Executable()\n\trequire.NoError(t, err)\n\n\tout := testutil.WriteSyncer{T: t}\n\n\t// Run the test binary with the name 'app' so that it runs main().\n\tcmd := exec.Command(exe)\n\tcmd.Args[0] = \"app\"\n\tcmd.Stdout = &out\n\tcmd.Stderr = &out\n\n\t// The program should exit with code 20.\n\terr = cmd.Run()\n\trequire.Error(t, err)\n\n\tvar exitErr *exec.ExitError\n\trequire.ErrorAs(t, err, &exitErr)\n\n\tassert.Equal(t, 20, exitErr.ExitCode())\n}\n"
  },
  {
    "path": "internal/fxclock/clock.go",
    "content": "// Copyright (c) 2024 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage fxclock\n\nimport (\n\t\"context\"\n\t\"sort\"\n\t\"sync\"\n\t\"time\"\n)\n\n// Clock defines how Fx accesses time.\n// We keep the interface pretty minimal.\ntype Clock interface {\n\tNow() time.Time\n\tSince(time.Time) time.Duration\n\tSleep(time.Duration)\n\tWithTimeout(context.Context, time.Duration) (context.Context, context.CancelFunc)\n}\n\n// System is the default implementation of Clock based on real time.\nvar System Clock = systemClock{}\n\ntype systemClock struct{}\n\nfunc (systemClock) Now() time.Time {\n\treturn time.Now()\n}\n\nfunc (systemClock) Since(t time.Time) time.Duration {\n\treturn time.Since(t)\n}\n\nfunc (systemClock) Sleep(d time.Duration) {\n\ttime.Sleep(d)\n}\n\nfunc (systemClock) WithTimeout(ctx context.Context, d time.Duration) (context.Context, context.CancelFunc) {\n\treturn context.WithTimeout(ctx, d)\n}\n\n// Mock adapted from\n// https://github.com/uber-go/zap/blob/7db06bc9b095571d3dc3d4eebdfbe4dd9bd20405/internal/ztest/clock.go.\n\n// Mock is a fake source of time.\n// It implements standard time operations,\n// but allows the user to control the passage of time.\n//\n// Use the [Add] method to progress time.\ntype Mock struct {\n\tmu  sync.RWMutex\n\tnow time.Time\n\n\t// The MockClock works by maintaining a list of waiters.\n\t// Each waiter knows the time at which it should be resolved.\n\t// When the clock advances, all waiters that are in range are resolved\n\t// in chronological order.\n\twaiters     []waiter\n\twaiterAdded *sync.Cond\n}\n\nvar _ Clock = (*Mock)(nil)\n\n// NewMock builds a new mock clock\n// using the current actual time as the initial time.\nfunc NewMock() *Mock {\n\tm := &Mock{now: time.Now()}\n\tm.waiterAdded = sync.NewCond(&m.mu)\n\treturn m\n}\n\n// Now reports the current time.\nfunc (c *Mock) Now() time.Time {\n\tc.mu.RLock()\n\tdefer c.mu.RUnlock()\n\treturn c.now\n}\n\n// Since reports the time elapsed since t.\n// This is short for Now().Sub(t).\nfunc (c *Mock) Since(t time.Time) time.Duration {\n\treturn c.Now().Sub(t)\n}\n\n// Sleep pauses the current goroutine for the given duration.\n//\n// With the mock clock, this will freeze\n// until the clock is advanced with [Add] past the deadline.\nfunc (c *Mock) Sleep(d time.Duration) {\n\tch := make(chan struct{})\n\tc.runAt(c.Now().Add(d), func() { close(ch) })\n\t<-ch\n}\n\n// WithTimeout returns a new context with a deadline of now + d.\n//\n// When the deadline is passed, the returned context's Done channel is closed\n// and the context's Err method returns context.DeadlineExceeded.\n// If the cancel function is called before the deadline is passed,\n// the context's Err method returns context.Canceled.\nfunc (c *Mock) WithTimeout(ctx context.Context, d time.Duration) (context.Context, context.CancelFunc) {\n\t// Unfortunately, we can't use context.WithCancelCause here.\n\t// Per its documentation (and verified by trying it):\n\t//\n\t//   ctx, cancel := context.WithCancelCause(parent)\n\t//   cancel(myError)\n\t//   ctx.Err() // returns context.Canceled\n\t//   context.Cause(ctx) // returns myError\n\t//\n\t// So it won't do for our purposes.\n\tdeadline := c.Now().Add(d)\n\tinner, cancelInner := context.WithCancel(ctx)\n\tdctx := &deadlineCtx{\n\t\tinner:       inner,\n\t\tcancelInner: cancelInner,\n\t\tdone:        make(chan struct{}),\n\t\tdeadline:    deadline,\n\t}\n\tctx = dctx\n\n\tc.runAt(deadline, func() {\n\t\tdctx.cancel(context.DeadlineExceeded)\n\t})\n\treturn ctx, func() { dctx.cancel(context.Canceled) }\n}\n\ntype deadlineCtx struct {\n\tinner       context.Context\n\tcancelInner func()\n\n\tdone     chan struct{}\n\tdeadline time.Time\n\n\tmu  sync.Mutex // guards err; the rest is immutable\n\terr error\n}\n\nvar _ context.Context = (*deadlineCtx)(nil)\n\nfunc (c *deadlineCtx) Deadline() (deadline time.Time, ok bool) { return c.deadline, true }\nfunc (c *deadlineCtx) Done() <-chan struct{}                   { return c.done }\nfunc (c *deadlineCtx) Value(key any) any                       { return c.inner.Value(key) }\n\nfunc (c *deadlineCtx) Err() error {\n\tc.mu.Lock()\n\tdefer c.mu.Unlock()\n\treturn c.err\n}\n\nfunc (c *deadlineCtx) cancel(err error) {\n\tc.mu.Lock()\n\tif c.err == nil {\n\t\tc.err = err\n\t\tclose(c.done)\n\t\tc.cancelInner()\n\t}\n\tc.mu.Unlock()\n}\n\n// runAt schedules the given function to be run at the given time.\n// The function runs without a lock held, so it may schedule more work.\nfunc (c *Mock) runAt(t time.Time, fn func()) {\n\tc.mu.Lock()\n\tdefer c.mu.Unlock()\n\n\tc.waiters = append(c.waiters, waiter{until: t, fn: fn})\n\tc.waiterAdded.Broadcast()\n}\n\n// AwaitScheduled blocks until there are at least N\n// operations scheduled for the future.\nfunc (c *Mock) AwaitScheduled(n int) {\n\tc.mu.Lock()\n\tdefer c.mu.Unlock()\n\n\t// Note: waiterAdded is associated with c.mu,\n\t// the same lock we're holding here.\n\t//\n\t// When we call Wait(), it'll release the lock\n\t// and block until signaled by runAt,\n\t// at which point it'll reacquire the lock\n\t// (waiting until runAt has released it).\n\tfor len(c.waiters) < n {\n\t\tc.waiterAdded.Wait()\n\t}\n}\n\ntype waiter struct {\n\tuntil time.Time\n\tfn    func()\n}\n\n// Add progresses time by the given duration.\n// Other operations waiting for the time to advance\n// will be resolved if they are within range.\n//\n// Side effects of operations waiting for the time to advance\n// will take effect on a best-effort basis.\n// Avoid racing with operations that have side effects.\n//\n// Panics if the duration is negative.\nfunc (c *Mock) Add(d time.Duration) {\n\tif d < 0 {\n\t\tpanic(\"cannot add negative duration\")\n\t}\n\n\tc.mu.Lock()\n\tdefer c.mu.Unlock()\n\n\tsort.Slice(c.waiters, func(i, j int) bool {\n\t\treturn c.waiters[i].until.Before(c.waiters[j].until)\n\t})\n\n\tnewTime := c.now.Add(d)\n\t// newTime won't be recorded until the end of this method.\n\t// This ensures that any waiters that are resolved\n\t// are resolved at the time they were expecting.\n\n\tfor len(c.waiters) > 0 {\n\t\tw := c.waiters[0]\n\t\tif w.until.After(newTime) {\n\t\t\tbreak\n\t\t}\n\t\tc.waiters[0] = waiter{} // avoid memory leak\n\t\tc.waiters = c.waiters[1:]\n\n\t\t// The waiter is within range.\n\t\t// Travel to the time of the waiter and resolve it.\n\t\tc.now = w.until\n\n\t\t// The waiter may schedule more work\n\t\t// so we must release the lock.\n\t\tc.mu.Unlock()\n\t\tw.fn()\n\t\t// Sleeping here is necessary to let the side effects of waiters\n\t\t// take effect before we continue.\n\t\ttime.Sleep(1 * time.Millisecond)\n\t\tc.mu.Lock()\n\t}\n\n\tc.now = newTime\n}\n"
  },
  {
    "path": "internal/fxclock/clock_test.go",
    "content": "// Copyright (c) 2024 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage fxclock\n\nimport (\n\t\"context\"\n\t\"sync\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestSystemClock(t *testing.T) {\n\tclock := System\n\ttestClock(t, System, clock.Sleep)\n}\n\nfunc TestMockClock(t *testing.T) {\n\tclock := NewMock()\n\ttestClock(t, clock, clock.Add)\n}\n\nfunc testClock(t *testing.T, clock Clock, advance func(d time.Duration)) {\n\tnow := clock.Now()\n\tassert.False(t, now.IsZero())\n\n\tt.Run(\"Since\", func(t *testing.T) {\n\t\tadvance(1 * time.Millisecond)\n\t\tassert.NotZero(t, clock.Since(now), \"time must have advanced\")\n\t})\n\n\tt.Run(\"Sleep\", func(t *testing.T) {\n\t\tstart := clock.Now()\n\n\t\tgo func() {\n\t\t\t// For the mock clock, there's a chance that advance will be\n\t\t\t// too fast and the Sleep will block forever, waiting for\n\t\t\t// another advance. The mock clock provides\n\t\t\t// AwaitScheduled to help with this.\n\t\t\t//\n\t\t\t// Since that function is not available on the system clock,\n\t\t\t// we'll use upcasting to check for it.\n\t\t\tif awaiter, ok := clock.(interface{ AwaitScheduled(int) }); ok {\n\t\t\t\tawaiter.AwaitScheduled(1)\n\t\t\t}\n\n\t\t\tadvance(1 * time.Millisecond)\n\t\t}()\n\t\tclock.Sleep(1 * time.Millisecond)\n\n\t\tassert.NotZero(t, clock.Since(start), \"time must have advanced\")\n\t})\n\n\tt.Run(\"WithTimeout\", func(t *testing.T) {\n\t\tctx, cancel := clock.WithTimeout(context.Background(), 1*time.Millisecond)\n\t\tdefer cancel()\n\n\t\tt.Run(\"Deadline\", func(t *testing.T) {\n\t\t\tdl, ok := ctx.Deadline()\n\t\t\tassert.True(t, ok, \"must have a deadline\")\n\t\t\tassert.True(t, dl.After(now), \"deadline must be in the future\")\n\t\t})\n\n\t\tadvance(1 * time.Millisecond)\n\n\t\tselect {\n\t\tcase <-ctx.Done():\n\t\t\tassert.Error(t, ctx.Err(), \"done context must error\")\n\t\t\tassert.ErrorIs(t, ctx.Err(), context.DeadlineExceeded,\n\t\t\t\t\"context must have exceeded its deadline\")\n\n\t\tcase <-time.After(10 * time.Millisecond):\n\t\t\tt.Fatal(\"expected context to be done\")\n\t\t}\n\t})\n\n\tt.Run(\"WithTimeout/Value\", func(t *testing.T) {\n\t\ttype contextKey string\n\t\tkey := contextKey(\"foo\")\n\n\t\tctx1 := context.WithValue(context.Background(), key, \"bar\")\n\n\t\tctx2, cancel := clock.WithTimeout(ctx1, 1*time.Millisecond)\n\t\tdefer cancel()\n\n\t\tassert.Equal(t, \"bar\", ctx2.Value(key), \"value must be preserved\")\n\t})\n\n\tt.Run(\"WithTimeout/Cancel\", func(t *testing.T) {\n\t\tctx, cancel := clock.WithTimeout(context.Background(), 1*time.Millisecond)\n\t\tcancel()\n\n\t\tselect {\n\t\tcase <-ctx.Done():\n\t\t\tassert.Error(t, ctx.Err(), \"done context must error\")\n\t\t\tassert.ErrorIs(t, ctx.Err(), context.Canceled,\n\t\t\t\t\"context must have been canceled\")\n\n\t\tcase <-time.After(10 * time.Millisecond):\n\t\t\tt.Fatal(\"expected context to be done\")\n\t\t}\n\t})\n}\n\nfunc TestMock_Sleep(t *testing.T) {\n\tclock := NewMock()\n\n\tch := make(chan struct{})\n\tgo func() {\n\t\tclock.Sleep(2 * time.Millisecond)\n\t\tclose(ch)\n\t}()\n\n\t// We cannot advance time until we're certain\n\t// that the Sleep call has started waiting.\n\t// Otherwise, we'll advance that one millisecond,\n\t// and then the Sleep will start waiting for another Advance,\n\t// which will never come.\n\t//\n\t// AwaitScheduled will block until there is at least one\n\t// scheduled event.\n\tclock.AwaitScheduled(1)\n\n\t// Advance only one millisecond, the Sleep should not return.\n\tclock.Add(1 * time.Millisecond)\n\tselect {\n\tcase <-ch:\n\t\tt.Fatal(\"sleep should not have returned\")\n\tcase <-time.After(1 * time.Millisecond):\n\t\t// ok\n\t}\n\n\t// Avance to the next millisecond, the Sleep should return.\n\tclock.Add(1 * time.Millisecond)\n\tselect {\n\tcase <-ch:\n\t\t// ok\n\tcase <-time.After(10 * time.Millisecond):\n\t\tt.Fatal(\"expected Sleep to return\")\n\t}\n}\n\nfunc TestMock_AddNegative(t *testing.T) {\n\tclock := NewMock()\n\tassert.Panics(t, func() { clock.Add(-1) })\n}\n\nfunc TestMock_ManySleepers(t *testing.T) {\n\tconst N = 100\n\n\tclock := NewMock()\n\n\tvar wg sync.WaitGroup\n\twg.Add(N)\n\tfor range N {\n\t\tgo func() {\n\t\t\tdefer wg.Done()\n\n\t\t\tclock.Sleep(1 * time.Millisecond)\n\t\t}()\n\t}\n\n\tclock.AwaitScheduled(N)\n\tclock.Add(1 * time.Millisecond)\n\n\tdone := make(chan struct{})\n\tgo func() {\n\t\tdefer close(done)\n\t\twg.Wait()\n\t}()\n\n\tselect {\n\tcase <-done:\n\t\t// ok\n\tcase <-time.After(10 * time.Millisecond):\n\t\tt.Fatal(\"expected all sleepers to be done\")\n\t}\n}\n"
  },
  {
    "path": "internal/fxlog/default.go",
    "content": "// Copyright (c) 2021 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage fxlog\n\nimport (\n\t\"io\"\n\n\t\"go.uber.org/fx/fxevent\"\n)\n\n// DefaultLogger constructs a Logger out of io.Writer.\nfunc DefaultLogger(w io.Writer) fxevent.Logger {\n\treturn &fxevent.ConsoleLogger{W: w}\n}\n"
  },
  {
    "path": "internal/fxlog/default_test.go",
    "content": "// Copyright (c) 2020-2021 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage fxlog\n\nimport (\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"go.uber.org/fx/internal/testutil\"\n\t\"go.uber.org/goleak\"\n)\n\nfunc TestNew(t *testing.T) {\n\tt.Parallel()\n\n\tassert.NotPanics(t, func() {\n\t\tDefaultLogger(testutil.WriteSyncer{T: t})\n\t})\n}\n\nfunc TestMain(m *testing.M) {\n\tgoleak.VerifyTestMain(m)\n}\n"
  },
  {
    "path": "internal/fxlog/foovendor/foovendor.go",
    "content": "// Copyright (c) 2019 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage foovendor\n\n// New is a test case for vendor path shortening\nfunc New() string { return \"foovendor\" }\n"
  },
  {
    "path": "internal/fxlog/sample.git/sample.go",
    "content": "// Copyright (c) 2019 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage sample\n\n// New is a test constructor for a string\nfunc New() (string, error) {\n\treturn \"Hi\", nil\n}\n"
  },
  {
    "path": "internal/fxlog/spy.go",
    "content": "// Copyright (c) 2020-2021 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage fxlog\n\nimport (\n\t\"reflect\"\n\t\"sync\"\n\n\t\"go.uber.org/fx/fxevent\"\n)\n\n// Events is a list of events captured by fxlog.Spy.\ntype Events []fxevent.Event\n\n// Len returns the number of events in this list.\nfunc (es Events) Len() int { return len(es) }\n\n// SelectByTypeName returns a new list with only events matching the specified\n// type.\nfunc (es Events) SelectByTypeName(name string) Events {\n\tvar out Events\n\tfor _, e := range es {\n\t\tif reflect.TypeOf(e).Elem().Name() == name {\n\t\t\tout = append(out, e)\n\t\t}\n\t}\n\treturn out\n}\n\n// Spy is an Fx event logger that captures emitted events and/or logged\n// statements. It may be used in tests of Fx logs.\ntype Spy struct {\n\tmu     sync.RWMutex\n\tevents Events\n}\n\nvar _ fxevent.Logger = &Spy{}\n\n// LogEvent appends an Event.\nfunc (s *Spy) LogEvent(event fxevent.Event) {\n\ts.mu.Lock()\n\ts.events = append(s.events, event)\n\ts.mu.Unlock()\n}\n\n// Events returns all captured events.\nfunc (s *Spy) Events() Events {\n\ts.mu.RLock()\n\tdefer s.mu.RUnlock()\n\n\tevents := make(Events, len(s.events))\n\tcopy(events, s.events)\n\treturn events\n}\n\n// EventTypes returns all captured event types.\nfunc (s *Spy) EventTypes() []string {\n\ts.mu.RLock()\n\tdefer s.mu.RUnlock()\n\n\ttypes := make([]string, len(s.events))\n\tfor i, e := range s.events {\n\t\ttypes[i] = reflect.TypeOf(e).Elem().Name()\n\t}\n\treturn types\n}\n\n// Reset clears all messages and events from the Spy.\nfunc (s *Spy) Reset() {\n\ts.mu.Lock()\n\ts.events = s.events[:0]\n\ts.mu.Unlock()\n}\n"
  },
  {
    "path": "internal/fxlog/spy_test.go",
    "content": "// Copyright (c) 2020 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage fxlog\n\nimport (\n\t\"fmt\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"go.uber.org/fx/fxevent\"\n)\n\nfunc TestSpy(t *testing.T) {\n\tt.Parallel()\n\n\tvar s Spy\n\n\tt.Run(\"empty spy\", func(t *testing.T) {\n\t\tassert.Empty(t, s.Events(), \"events must be empty\")\n\t\tassert.Zero(t, s.Events().Len(), \"events length must be zero\")\n\t\tassert.Empty(t, s.EventTypes(), \"event types must be empty\")\n\t})\n\n\ts.LogEvent(&fxevent.Started{})\n\tt.Run(\"use after reset\", func(t *testing.T) {\n\t\tassert.Equal(t, \"Started\", s.EventTypes()[0])\n\t})\n\n\ts.LogEvent(&fxevent.Provided{Err: fmt.Errorf(\"some error\")})\n\tt.Run(\"some error\", func(t *testing.T) {\n\t\tassert.Equal(t, 1, s.Events().SelectByTypeName(\"Provided\").Len())\n\t\tassert.Equal(t, \"Provided\", s.EventTypes()[1])\n\t})\n\n\ts.Reset()\n\tt.Run(\"reset\", func(t *testing.T) {\n\t\tassert.Empty(t, s.Events(), \"events must be empty\")\n\t\tassert.Empty(t, s.EventTypes(), \"event types must be empty\")\n\t})\n\n\ts.LogEvent(&fxevent.Started{})\n\tt.Run(\"use after reset\", func(t *testing.T) {\n\t\tassert.Equal(t, \"Started\", s.EventTypes()[0])\n\t})\n}\n"
  },
  {
    "path": "internal/fxreflect/fxreflect.go",
    "content": "// Copyright (c) 2019-2021 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage fxreflect\n\nimport (\n\t\"fmt\"\n\t\"net/url\"\n\t\"reflect\"\n\t\"regexp\"\n\t\"runtime\"\n\t\"strings\"\n)\n\n// Match from beginning of the line until the first `vendor/` (non-greedy)\nvar vendorRe = regexp.MustCompile(\"^.*?/vendor/\")\n\n// sanitize makes the function name suitable for logging display. It removes\n// url-encoded elements from the `dot.git` package names and shortens the\n// vendored paths.\nfunc sanitize(function string) string {\n\t// Use the stdlib to un-escape any package import paths which can happen\n\t// in the case of the \"dot-git\" postfix. Seems like a bug in stdlib =/\n\tif unescaped, err := url.QueryUnescape(function); err == nil {\n\t\tfunction = unescaped\n\t}\n\n\t// strip everything prior to the vendor\n\treturn vendorRe.ReplaceAllString(function, \"vendor/\")\n}\n\n// Caller returns the formatted calling func name\nfunc Caller() string {\n\treturn CallerStack(1, 0).CallerName()\n}\n\n// FuncName returns a funcs formatted name\nfunc FuncName(fn any) string {\n\tfnV := reflect.ValueOf(fn)\n\tif fnV.Kind() != reflect.Func {\n\t\treturn fmt.Sprint(fn)\n\t}\n\n\tfunction := runtime.FuncForPC(fnV.Pointer()).Name()\n\treturn fmt.Sprintf(\"%s()\", sanitize(function))\n}\n\n// Ascend the call stack until we leave the Fx production code. This allows us\n// to avoid hard-coding a frame skip, which makes this code work well even\n// when it's wrapped.\nfunc shouldIgnoreFrame(f Frame) bool {\n\t// Treat test files as leafs.\n\tif strings.Contains(f.File, \"_test.go\") {\n\t\treturn false\n\t}\n\n\t// The unique, fully-qualified name for all functions begins with\n\t// \"{{importPath}}.\". We'll ignore Fx and its subpackages.\n\ts := strings.TrimPrefix(f.Function, \"go.uber.org/fx\")\n\tif len(s) > 0 && s[0] == '.' || s[0] == '/' {\n\t\t// We want to match,\n\t\t//   go.uber.org/fx.Foo\n\t\t//   go.uber.org/fx/something.Foo\n\t\t// But not, go.uber.org/fxfoo\n\t\treturn true\n\t}\n\n\treturn false\n}\n"
  },
  {
    "path": "internal/fxreflect/fxreflect_test.go",
    "content": "// Copyright (c) 2019-2021 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage fxreflect\n\nimport (\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"go.uber.org/goleak\"\n)\n\nfunc TestCaller(t *testing.T) {\n\tt.Parallel()\n\n\tassert.Equal(t, \"go.uber.org/fx/internal/fxreflect.TestCaller\", Caller())\n}\n\nfunc someFunc() {}\n\nfunc TestFuncName(t *testing.T) {\n\tt.Parallel()\n\n\ttests := []struct {\n\t\tdesc string\n\t\tgive any\n\t\twant string\n\t}{\n\t\t{\n\t\t\tdesc: \"function\",\n\t\t\tgive: someFunc,\n\t\t\twant: \"go.uber.org/fx/internal/fxreflect.someFunc()\",\n\t\t},\n\t\t{\n\t\t\tdesc: \"not a function\",\n\t\t\tgive: 42,\n\t\t\twant: \"42\",\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.desc, func(t *testing.T) {\n\t\t\tt.Parallel()\n\n\t\t\tassert.Equal(t, tt.want, FuncName(tt.give))\n\t\t})\n\t}\n}\n\nfunc TestSanitizeFuncNames(t *testing.T) {\n\tt.Parallel()\n\n\tcases := []struct {\n\t\tname     string\n\t\tinput    string\n\t\texpected string\n\t}{\n\t\t{\n\t\t\t\"url encoding\",\n\t\t\t\"go.uber.org/fx/sample%2egit/someFunc\",\n\t\t\t\"go.uber.org/fx/sample.git/someFunc\",\n\t\t},\n\t\t{\n\t\t\t\"vendor removal\",\n\t\t\t\"go.uber.org/fx/vendor/github.com/some/lib.SomeFunc\",\n\t\t\t\"vendor/github.com/some/lib.SomeFunc\",\n\t\t},\n\t\t{\n\t\t\t\"package happens to be named vendor is untouched\",\n\t\t\t\"go.uber.org/fx/foovendor/someFunc\",\n\t\t\t\"go.uber.org/fx/foovendor/someFunc\",\n\t\t},\n\t}\n\tfor _, c := range cases {\n\t\tt.Run(c.name, func(t *testing.T) {\n\t\t\tt.Parallel()\n\n\t\t\tassert.Equal(t, c.expected, sanitize(c.input))\n\t\t})\n\t}\n}\n\nfunc TestMain(m *testing.M) {\n\tgoleak.VerifyTestMain(m)\n}\n"
  },
  {
    "path": "internal/fxreflect/stack.go",
    "content": "// Copyright (c) 2019 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage fxreflect\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"runtime\"\n\t\"strings\"\n)\n\n// Frame holds information about a single frame in the call stack.\ntype Frame struct {\n\t// Unique, package path-qualified name for the function of this call\n\t// frame.\n\tFunction string\n\n\t// File and line number of our location in the frame.\n\t//\n\t// Note that the line number does not refer to where the function was\n\t// defined but where in the function the next call was made.\n\tFile string\n\tLine int\n}\n\nfunc (f Frame) String() string {\n\t// This takes the following forms.\n\t//  (path/to/file.go)\n\t//  (path/to/file.go:42)\n\t//  path/to/package.MyFunction\n\t//  path/to/package.MyFunction (path/to/file.go)\n\t//  path/to/package.MyFunction (path/to/file.go:42)\n\n\tvar sb strings.Builder\n\tsb.WriteString(f.Function)\n\tif len(f.File) > 0 {\n\t\tif sb.Len() > 0 {\n\t\t\tsb.WriteRune(' ')\n\t\t}\n\t\tfmt.Fprintf(&sb, \"(%v\", f.File)\n\t\tif f.Line > 0 {\n\t\t\tfmt.Fprintf(&sb, \":%d\", f.Line)\n\t\t}\n\t\tsb.WriteRune(')')\n\t}\n\n\tif sb.Len() == 0 {\n\t\treturn \"unknown\"\n\t}\n\n\treturn sb.String()\n}\n\nconst _defaultCallersDepth = 8\n\n// Stack is a stack of call frames.\n//\n// Formatted with %v, the output is in a single-line, in the form,\n//\n//\tfoo/bar.Baz() (path/to/foo.go:42); bar/baz.Qux() (bar/baz/qux.go:12); ...\n//\n// Formatted with %+v, the output is in the form,\n//\n//\tfoo/bar.Baz()\n//\t\tpath/to/foo.go:42\n//\tbar/baz.Qux()\n//\t\tbar/baz/qux.go:12\ntype Stack []Frame\n\n// String returns a single-line, semi-colon representation of a Stack.\n// For a list of strings where each represents one frame, use Strings.\n// For a cleaner multi-line representation, use %+v.\nfunc (fs Stack) String() string {\n\treturn strings.Join(fs.Strings(), \"; \")\n}\n\n// Strings returns a list of strings, each representing a frame in the stack.\n// Each line will be in the form,\n//\n//\tfoo/bar.Baz() (path/to/foo.go:42)\nfunc (fs Stack) Strings() []string {\n\titems := make([]string, len(fs))\n\tfor i, f := range fs {\n\t\titems[i] = f.String()\n\t}\n\treturn items\n}\n\n// Format implements fmt.Formatter to handle \"%+v\".\nfunc (fs Stack) Format(w fmt.State, c rune) {\n\tif !w.Flag('+') {\n\t\t// Without %+v, fall back to String().\n\t\tio.WriteString(w, fs.String())\n\t\treturn\n\t}\n\n\tfor _, f := range fs {\n\t\tfmt.Fprintln(w, f.Function)\n\t\tfmt.Fprintf(w, \"\\t%v:%v\\n\", f.File, f.Line)\n\t}\n}\n\n// CallerName returns the name of the first caller in this stack that isn't\n// owned by the Fx library.\nfunc (fs Stack) CallerName() string {\n\tfor _, f := range fs {\n\t\tif shouldIgnoreFrame(f) {\n\t\t\tcontinue\n\t\t}\n\t\treturn f.Function\n\t}\n\treturn \"n/a\"\n}\n\n// CallerStack returns the call stack for the calling function, up to depth frames\n// deep, skipping the provided number of frames, not including Callers itself.\n//\n// If zero, depth defaults to 8.\nfunc CallerStack(skip, depth int) Stack {\n\tif depth <= 0 {\n\t\tdepth = _defaultCallersDepth\n\t}\n\n\tpcs := make([]uintptr, depth)\n\n\t// +2 to skip this frame and runtime.Callers.\n\tn := runtime.Callers(skip+2, pcs)\n\tpcs = pcs[:n] // truncate to number of frames actually read\n\n\tresult := make([]Frame, 0, n)\n\tframes := runtime.CallersFrames(pcs)\n\tfor f, more := frames.Next(); more; f, more = frames.Next() {\n\t\tresult = append(result, Frame{\n\t\t\tFunction: sanitize(f.Function),\n\t\t\tFile:     f.File,\n\t\t\tLine:     f.Line,\n\t\t})\n\t}\n\treturn result\n}\n"
  },
  {
    "path": "internal/fxreflect/stack_test.go",
    "content": "// Copyright (c) 2019 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage fxreflect\n\nimport (\n\t\"fmt\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestStack(t *testing.T) {\n\tt.Parallel()\n\n\t// NOTE:\n\t// We don't assert the length of the stack because we cannot make\n\t// guarantees about how many frames the test runner is allowed to\n\t// introduce.\n\n\tt.Run(\"default\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tframes := CallerStack(0, 0)\n\t\trequire.NotEmpty(t, frames)\n\t\tf := frames[0]\n\t\tassert.Equal(t, \"go.uber.org/fx/internal/fxreflect.TestStack.func1\", f.Function)\n\t\tassert.Contains(t, f.File, \"internal/fxreflect/stack_test.go\")\n\t\tassert.NotZero(t, f.Line)\n\t})\n\n\tt.Run(\"skip\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\t// Introduce a few frames and skip 2.\n\t\tframes := func() []Frame {\n\t\t\treturn func() []Frame {\n\t\t\t\treturn CallerStack(2, 0)\n\t\t\t}()\n\t\t}()\n\n\t\trequire.NotEmpty(t, frames)\n\t\tf := frames[0]\n\t\tassert.Equal(t, \"go.uber.org/fx/internal/fxreflect.TestStack.func2\", f.Function)\n\t\tassert.Contains(t, f.File, \"internal/fxreflect/stack_test.go\")\n\t\tassert.NotZero(t, f.Line)\n\t})\n}\n\nfunc TestDeepStack(t *testing.T) {\n\tt.Run(\"nest\", func(t *testing.T) {\n\t\t// Introduce a few frames.\n\t\tframes := func() []Frame {\n\t\t\treturn func() []Frame {\n\t\t\t\treturn CallerStack(0, 0)\n\t\t\t}()\n\t\t}()\n\n\t\trequire.True(t, len(frames) > 3, \"expected at least three frames\")\n\t\tfor i, name := range []string{\"func1.TestDeepStack.func1.1.2\", \"func1.1\", \"func1\"} {\n\t\t\tf := frames[i]\n\t\t\tassert.Equal(t, \"go.uber.org/fx/internal/fxreflect.TestDeepStack.\"+name, f.Function)\n\t\t\tassert.Contains(t, f.File, \"internal/fxreflect/stack_test.go\")\n\t\t\tassert.NotZero(t, f.Line)\n\t\t}\n\t})\n}\n\nfunc TestStackCallerName(t *testing.T) {\n\tt.Parallel()\n\n\ttests := []struct {\n\t\tdesc string\n\t\tgive Stack\n\t\twant string\n\t}{\n\t\t{desc: \"empty\", want: \"n/a\"},\n\t\t{\n\t\t\tdesc: \"skip Fx components\",\n\t\t\tgive: Stack{\n\t\t\t\t{\n\t\t\t\t\tFunction: \"go.uber.org/fx.Foo()\",\n\t\t\t\t\tFile:     \"go.uber.org/fx/foo.go\",\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tFunction: \"foo/bar.Baz()\",\n\t\t\t\t\tFile:     \"foo/bar/baz.go\",\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: \"foo/bar.Baz()\",\n\t\t},\n\t\t{\n\t\t\tdesc: \"skip Fx in wrong directory\",\n\t\t\tgive: Stack{\n\t\t\t\t{\n\t\t\t\t\tFunction: \"go.uber.org/fx.Foo()\",\n\t\t\t\t\tFile:     \"fx/foo.go\",\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tFunction: \"foo/bar.Baz()\",\n\t\t\t\t\tFile:     \"foo/bar/baz.go\",\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: \"foo/bar.Baz()\",\n\t\t},\n\t\t{\n\t\t\tdesc: \"skip Fx subpackage\",\n\t\t\tgive: Stack{\n\t\t\t\t{\n\t\t\t\t\tFunction: \"go.uber.org/fx/internal/fxreflect.Foo()\",\n\t\t\t\t\tFile:     \"fx/internal/fxreflect/foo.go\",\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tFunction: \"foo/bar.Baz()\",\n\t\t\t\t\tFile:     \"foo/bar/baz.go\",\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: \"foo/bar.Baz()\",\n\t\t},\n\t\t{\n\t\t\tdesc: \"don't skip Fx tests\",\n\t\t\tgive: Stack{\n\t\t\t\t{\n\t\t\t\t\tFunction: \"some/thing.Foo()\",\n\t\t\t\t\tFile:     \"go.uber.org/fx/foo_test.go\",\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: \"some/thing.Foo()\",\n\t\t},\n\t\t{\n\t\t\tdesc: \"don't skip fx prefix\",\n\t\t\tgive: Stack{\n\t\t\t\t{\n\t\t\t\t\tFunction: \"go.uber.org/fxfoo.Bar()\",\n\t\t\t\t\tFile:     \"go.uber.org/fxfoo/bar.go\",\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: \"go.uber.org/fxfoo.Bar()\",\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.desc, func(t *testing.T) {\n\t\t\tt.Parallel()\n\n\t\t\tassert.Equal(t, tt.want, tt.give.CallerName())\n\t\t})\n\t}\n}\n\nfunc TestFrameString(t *testing.T) {\n\tt.Parallel()\n\n\ttests := []struct {\n\t\tdesc string\n\t\tgive Frame\n\t\twant string\n\t}{\n\t\t{\n\t\t\tdesc: \"zero\",\n\t\t\tgive: Frame{},\n\t\t\twant: \"unknown\",\n\t\t},\n\t\t{\n\t\t\tdesc: \"file and line\",\n\t\t\tgive: Frame{File: \"foo.go\", Line: 42},\n\t\t\twant: \"(foo.go:42)\",\n\t\t},\n\t\t{\n\t\t\tdesc: \"file only\",\n\t\t\tgive: Frame{File: \"foo.go\"},\n\t\t\twant: \"(foo.go)\",\n\t\t},\n\t\t{\n\t\t\tdesc: \"function only\",\n\t\t\tgive: Frame{Function: \"foo\"},\n\t\t\twant: \"foo\",\n\t\t},\n\t\t{\n\t\t\tdesc: \"function and file\",\n\t\t\tgive: Frame{Function: \"foo\", File: \"bar.go\"},\n\t\t\twant: \"foo (bar.go)\",\n\t\t},\n\t\t{\n\t\t\tdesc: \"function and line\",\n\t\t\tgive: Frame{Function: \"foo\", Line: 42},\n\t\t\twant: \"foo\", // line without file is meaningless\n\t\t},\n\t\t{\n\t\t\tdesc: \"function, file, and line\",\n\t\t\tgive: Frame{Function: \"foo\", File: \"bar.go\", Line: 42},\n\t\t\twant: \"foo (bar.go:42)\",\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.desc, func(t *testing.T) {\n\t\t\tt.Parallel()\n\n\t\t\tassert.Equal(t, tt.want, tt.give.String())\n\t\t})\n\t}\n}\n\nfunc TestStackFormat(t *testing.T) {\n\tt.Parallel()\n\n\tstack := Stack{\n\t\t{\n\t\t\tFunction: \"path/to/module.SomeFunction()\",\n\t\t\tFile:     \"path/to/file.go\",\n\t\t\tLine:     42,\n\t\t},\n\t\t{\n\t\t\tFunction: \"path/to/another/module.AnotherFunction()\",\n\t\t\tFile:     \"path/to/another/file.go\",\n\t\t\tLine:     12,\n\t\t},\n\t}\n\n\tt.Run(\"single line\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tassert.Equal(t,\n\t\t\t\"path/to/module.SomeFunction() (path/to/file.go:42); \"+\n\t\t\t\t\"path/to/another/module.AnotherFunction() (path/to/another/file.go:12)\",\n\t\t\tfmt.Sprintf(\"%v\", stack))\n\t})\n\n\tt.Run(\"multi line\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tassert.Equal(t, `path/to/module.SomeFunction()\n\tpath/to/file.go:42\npath/to/another/module.AnotherFunction()\n\tpath/to/another/file.go:12\n`, fmt.Sprintf(\"%+v\", stack))\n\t})\n\n\tt.Run(\"strings\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tassert.Equal(t,\n\t\t\t[]string{\n\t\t\t\t\"path/to/module.SomeFunction() (path/to/file.go:42)\",\n\t\t\t\t\"path/to/another/module.AnotherFunction() (path/to/another/file.go:12)\",\n\t\t\t},\n\t\t\tstack.Strings(),\n\t\t)\n\t})\n}\n"
  },
  {
    "path": "internal/leaky_test/leaky_test.go",
    "content": "// Copyright (c) 2022 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\n// leaky_test contains tests that are explicitly leaking goroutines because they\n// trigger a panic on purpose.\n// To prevent these from making it difficult for us to use goleak for tests in\n// fx_test, we contain these here.\npackage leaky_test\n\nimport (\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\t\"go.uber.org/fx\"\n)\n\nfunc TestRecoverFromPanicsOption(t *testing.T) {\n\tt.Parallel()\n\n\tt.Run(\"CannotGiveToModule\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tmod := fx.Module(\"MyModule\", fx.RecoverFromPanics())\n\t\terr := fx.New(mod).Err()\n\t\trequire.Error(t, err)\n\t\trequire.Contains(t, err.Error(),\n\t\t\t\"fx.RecoverFromPanics Option should be passed to top-level App, \"+\n\t\t\t\t\"not to fx.Module\")\n\t})\n\trun := func(withOption bool) {\n\t\topts := []fx.Option{\n\t\t\tfx.Provide(func() int {\n\t\t\t\tpanic(\"terrible sorrow\")\n\t\t\t}),\n\t\t\tfx.Invoke(func(a int) {}),\n\t\t}\n\t\tif withOption {\n\t\t\topts = append(opts, fx.RecoverFromPanics())\n\t\t\tapp := fx.New(opts...)\n\t\t\terr := app.Err()\n\t\t\trequire.Error(t, err)\n\t\t\tassert.Contains(t, err.Error(),\n\t\t\t\t`panic: \"terrible sorrow\" in func: \"go.uber.org/fx/internal/leaky_test_test\".TestRecoverFromPanicsOption.`)\n\t\t} else {\n\t\t\tassert.Panics(t, func() { fx.New(opts...) },\n\t\t\t\t\"expected panic without RecoverFromPanics() option\")\n\t\t}\n\t}\n\n\tt.Run(\"WithoutOption\", func(t *testing.T) { run(false) })\n\tt.Run(\"WithOption\", func(t *testing.T) { run(true) })\n}\n"
  },
  {
    "path": "internal/lifecycle/lifecycle.go",
    "content": "// Copyright (c) 2022 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage lifecycle\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"reflect\"\n\t\"strings\"\n\t\"sync\"\n\t\"time\"\n\n\t\"go.uber.org/fx/fxevent\"\n\t\"go.uber.org/fx/internal/fxclock\"\n\t\"go.uber.org/fx/internal/fxreflect\"\n\t\"go.uber.org/multierr\"\n)\n\n// Reflection types for each of the supported hook function signatures. These\n// are used in cases in which the Callable constraint matches a user-defined\n// function type that cannot be converted to an underlying function type with\n// a conventional conversion or type switch.\nvar (\n\t_reflFunc             = reflect.TypeOf(Func(nil))\n\t_reflErrorFunc        = reflect.TypeOf(ErrorFunc(nil))\n\t_reflContextFunc      = reflect.TypeOf(ContextFunc(nil))\n\t_reflContextErrorFunc = reflect.TypeOf(ContextErrorFunc(nil))\n)\n\n// Discrete function signatures that are allowed as part of a [Callable].\ntype (\n\t// A Func can be converted to a ContextErrorFunc.\n\tFunc = func()\n\t// An ErrorFunc can be converted to a ContextErrorFunc.\n\tErrorFunc = func() error\n\t// A ContextFunc can be converted to a ContextErrorFunc.\n\tContextFunc = func(context.Context)\n\t// A ContextErrorFunc is used as a [Hook.OnStart] or [Hook.OnStop]\n\t// function.\n\tContextErrorFunc = func(context.Context) error\n)\n\n// A Callable is a constraint that matches functions that are, or can be\n// converted to, functions suitable for a Hook.\n//\n// Callable must be identical to [fx.HookFunc].\ntype Callable interface {\n\t~Func | ~ErrorFunc | ~ContextFunc | ~ContextErrorFunc\n}\n\n// Wrap wraps x into a ContextErrorFunc suitable for a Hook.\nfunc Wrap[T Callable](x T) (ContextErrorFunc, string) {\n\tif x == nil {\n\t\treturn nil, \"\"\n\t}\n\n\tswitch fn := any(x).(type) {\n\tcase Func:\n\t\treturn func(context.Context) error {\n\t\t\tfn()\n\t\t\treturn nil\n\t\t}, fxreflect.FuncName(x)\n\tcase ErrorFunc:\n\t\treturn func(context.Context) error {\n\t\t\treturn fn()\n\t\t}, fxreflect.FuncName(x)\n\tcase ContextFunc:\n\t\treturn func(ctx context.Context) error {\n\t\t\tfn(ctx)\n\t\t\treturn nil\n\t\t}, fxreflect.FuncName(x)\n\tcase ContextErrorFunc:\n\t\treturn fn, fxreflect.FuncName(x)\n\t}\n\n\t// Since (1) we're already using reflect in Fx, (2) we're not particularly\n\t// concerned with performance, and (3) unsafe would require discrete build\n\t// targets for appengine (etc), just use reflect to convert user-defined\n\t// function types to their underlying function types and then call Wrap\n\t// again with the converted value.\n\treflVal := reflect.ValueOf(x)\n\tswitch {\n\tcase reflVal.CanConvert(_reflFunc):\n\t\treturn Wrap(reflVal.Convert(_reflFunc).Interface().(Func))\n\tcase reflVal.CanConvert(_reflErrorFunc):\n\t\treturn Wrap(reflVal.Convert(_reflErrorFunc).Interface().(ErrorFunc))\n\tcase reflVal.CanConvert(_reflContextFunc):\n\t\treturn Wrap(reflVal.Convert(_reflContextFunc).Interface().(ContextFunc))\n\tdefault:\n\t\t// Is already convertible to ContextErrorFunc.\n\t\treturn Wrap(reflVal.Convert(_reflContextErrorFunc).Interface().(ContextErrorFunc))\n\t}\n}\n\n// A Hook is a pair of start and stop callbacks, either of which can be nil,\n// plus a string identifying the supplier of the hook.\ntype Hook struct {\n\tOnStart     func(context.Context) error\n\tOnStop      func(context.Context) error\n\tOnStartName string\n\tOnStopName  string\n\n\tcallerFrame fxreflect.Frame\n}\n\ntype appState int\n\nconst (\n\tstopped appState = iota\n\tstarting\n\tincompleteStart\n\tstarted\n\tstopping\n)\n\nfunc (as appState) String() string {\n\tswitch as {\n\tcase stopped:\n\t\treturn \"stopped\"\n\tcase starting:\n\t\treturn \"starting\"\n\tcase incompleteStart:\n\t\treturn \"incompleteStart\"\n\tcase started:\n\t\treturn \"started\"\n\tcase stopping:\n\t\treturn \"stopping\"\n\tdefault:\n\t\treturn \"invalidState\"\n\t}\n}\n\n// Lifecycle coordinates application lifecycle hooks.\ntype Lifecycle struct {\n\tclock        fxclock.Clock\n\tlogger       fxevent.Logger\n\tstate        appState\n\thooks        []Hook\n\tnumStarted   int\n\tstartRecords HookRecords\n\tstopRecords  HookRecords\n\trunningHook  Hook\n\tmu           sync.Mutex\n}\n\n// New constructs a new Lifecycle.\nfunc New(logger fxevent.Logger, clock fxclock.Clock) *Lifecycle {\n\treturn &Lifecycle{logger: logger, clock: clock}\n}\n\n// Append adds a Hook to the lifecycle.\nfunc (l *Lifecycle) Append(hook Hook) {\n\t// Save the caller's stack frame to report file/line number.\n\tif f := fxreflect.CallerStack(2, 0); len(f) > 0 {\n\t\thook.callerFrame = f[0]\n\t}\n\tl.hooks = append(l.hooks, hook)\n}\n\n// Start runs all OnStart hooks, returning immediately if it encounters an\n// error.\nfunc (l *Lifecycle) Start(ctx context.Context) error {\n\tif ctx == nil {\n\t\treturn errors.New(\"called OnStart with nil context\")\n\t}\n\n\tl.mu.Lock()\n\tif l.state != stopped {\n\t\tdefer l.mu.Unlock()\n\t\treturn fmt.Errorf(\"attempted to start lifecycle when in state: %v\", l.state)\n\t}\n\tl.numStarted = 0\n\tl.state = starting\n\n\tl.startRecords = make(HookRecords, 0, len(l.hooks))\n\tl.mu.Unlock()\n\n\treturnState := incompleteStart\n\tdefer func() {\n\t\tl.mu.Lock()\n\t\tl.state = returnState\n\t\tl.mu.Unlock()\n\t}()\n\n\tfor _, hook := range l.hooks {\n\t\t// if ctx has cancelled, bail out of the loop.\n\t\tif err := ctx.Err(); err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tif hook.OnStart != nil {\n\t\t\tl.mu.Lock()\n\t\t\tl.runningHook = hook\n\t\t\tl.mu.Unlock()\n\n\t\t\truntime, err := l.runStartHook(ctx, hook)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\n\t\t\tl.mu.Lock()\n\t\t\tl.startRecords = append(l.startRecords, HookRecord{\n\t\t\t\tCallerFrame: hook.callerFrame,\n\t\t\t\tFunc:        hook.OnStart,\n\t\t\t\tRuntime:     runtime,\n\t\t\t})\n\t\t\tl.mu.Unlock()\n\t\t}\n\t\tl.numStarted++\n\t}\n\n\treturnState = started\n\treturn nil\n}\n\nfunc (l *Lifecycle) runStartHook(ctx context.Context, hook Hook) (runtime time.Duration, err error) {\n\tfuncName := hook.OnStartName\n\tif len(funcName) == 0 {\n\t\tfuncName = fxreflect.FuncName(hook.OnStart)\n\t}\n\n\tl.logger.LogEvent(&fxevent.OnStartExecuting{\n\t\tCallerName:   hook.callerFrame.Function,\n\t\tFunctionName: funcName,\n\t})\n\tdefer func() {\n\t\tl.logger.LogEvent(&fxevent.OnStartExecuted{\n\t\t\tCallerName:   hook.callerFrame.Function,\n\t\t\tFunctionName: funcName,\n\t\t\tRuntime:      runtime,\n\t\t\tErr:          err,\n\t\t})\n\t}()\n\n\tbegin := l.clock.Now()\n\terr = hook.OnStart(ctx)\n\treturn l.clock.Since(begin), err\n}\n\n// Stop runs any OnStop hooks whose OnStart counterpart succeeded. OnStop\n// hooks run in reverse order.\nfunc (l *Lifecycle) Stop(ctx context.Context) error {\n\tif ctx == nil {\n\t\treturn errors.New(\"called OnStop with nil context\")\n\t}\n\n\tl.mu.Lock()\n\tif l.state != started && l.state != incompleteStart && l.state != starting {\n\t\tdefer l.mu.Unlock()\n\t\treturn nil\n\t}\n\tl.state = stopping\n\tl.mu.Unlock()\n\n\tdefer func() {\n\t\tl.mu.Lock()\n\t\tl.state = stopped\n\t\tl.mu.Unlock()\n\t}()\n\n\tl.mu.Lock()\n\tl.stopRecords = make(HookRecords, 0, l.numStarted)\n\t// Take a snapshot of hook state to avoid races.\n\tallHooks := l.hooks[:]\n\tnumStarted := l.numStarted\n\tl.mu.Unlock()\n\n\t// Run backward from last successful OnStart.\n\tvar errs []error\n\tfor ; numStarted > 0; numStarted-- {\n\t\tif err := ctx.Err(); err != nil {\n\t\t\treturn err\n\t\t}\n\t\thook := allHooks[numStarted-1]\n\t\tif hook.OnStop == nil {\n\t\t\tcontinue\n\t\t}\n\n\t\tl.mu.Lock()\n\t\tl.runningHook = hook\n\t\tl.mu.Unlock()\n\n\t\truntime, err := l.runStopHook(ctx, hook)\n\t\tif err != nil {\n\t\t\t// For best-effort cleanup, keep going after errors.\n\t\t\terrs = append(errs, err)\n\t\t}\n\n\t\tl.mu.Lock()\n\t\tl.stopRecords = append(l.stopRecords, HookRecord{\n\t\t\tCallerFrame: hook.callerFrame,\n\t\t\tFunc:        hook.OnStop,\n\t\t\tRuntime:     runtime,\n\t\t})\n\t\tl.mu.Unlock()\n\t}\n\n\treturn multierr.Combine(errs...)\n}\n\nfunc (l *Lifecycle) runStopHook(ctx context.Context, hook Hook) (runtime time.Duration, err error) {\n\tfuncName := hook.OnStopName\n\tif len(funcName) == 0 {\n\t\tfuncName = fxreflect.FuncName(hook.OnStop)\n\t}\n\n\tl.logger.LogEvent(&fxevent.OnStopExecuting{\n\t\tCallerName:   hook.callerFrame.Function,\n\t\tFunctionName: funcName,\n\t})\n\tdefer func() {\n\t\tl.logger.LogEvent(&fxevent.OnStopExecuted{\n\t\t\tCallerName:   hook.callerFrame.Function,\n\t\t\tFunctionName: funcName,\n\t\t\tRuntime:      runtime,\n\t\t\tErr:          err,\n\t\t})\n\t}()\n\n\tbegin := l.clock.Now()\n\terr = hook.OnStop(ctx)\n\treturn l.clock.Since(begin), err\n}\n\n// RunningHookCaller returns the name of the hook that was running when a Start/Stop\n// hook timed out.\nfunc (l *Lifecycle) RunningHookCaller() string {\n\tl.mu.Lock()\n\tdefer l.mu.Unlock()\n\treturn l.runningHook.callerFrame.Function\n}\n\n// HookRecord keeps track of each Hook's execution time, the caller that appended the Hook, and function that ran as the Hook.\ntype HookRecord struct {\n\tCallerFrame fxreflect.Frame             // stack frame of the caller\n\tFunc        func(context.Context) error // function that ran as sanitized name\n\tRuntime     time.Duration               // how long the hook ran\n}\n\n// HookRecords is a Stringer wrapper of HookRecord slice.\ntype HookRecords []HookRecord\n\nfunc (rs HookRecords) Len() int {\n\treturn len(rs)\n}\n\nfunc (rs HookRecords) Less(i, j int) bool {\n\t// Sort by runtime, greater ones at top.\n\treturn rs[i].Runtime > rs[j].Runtime\n}\n\nfunc (rs HookRecords) Swap(i, j int) {\n\trs[i], rs[j] = rs[j], rs[i]\n}\n\n// Used for logging startup errors.\nfunc (rs HookRecords) String() string {\n\tvar b strings.Builder\n\tfor _, r := range rs {\n\t\tfmt.Fprintf(&b, \"%s took %v from %s\",\n\t\t\tfxreflect.FuncName(r.Func), r.Runtime, r.CallerFrame)\n\t}\n\treturn b.String()\n}\n\n// Format implements fmt.Formatter to handle \"%+v\".\nfunc (rs HookRecords) Format(w fmt.State, c rune) {\n\tif !w.Flag('+') {\n\t\t// Without %+v, fall back to String().\n\t\tio.WriteString(w, rs.String())\n\t\treturn\n\t}\n\n\tfor _, r := range rs {\n\t\tfmt.Fprintf(w, \"\\n%s took %v from:\\n\\t%+v\",\n\t\t\tfxreflect.FuncName(r.Func),\n\t\t\tr.Runtime,\n\t\t\tr.CallerFrame)\n\t}\n\tfmt.Fprintf(w, \"\\n\")\n}\n"
  },
  {
    "path": "internal/lifecycle/lifecycle_test.go",
    "content": "// Copyright (c) 2019 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage lifecycle\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"sort\"\n\t\"strings\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\t\"go.uber.org/fx/fxevent\"\n\t\"go.uber.org/fx/internal/fxclock\"\n\t\"go.uber.org/fx/internal/fxlog\"\n\t\"go.uber.org/fx/internal/fxreflect\"\n\t\"go.uber.org/fx/internal/testutil\"\n\t\"go.uber.org/goleak\"\n\t\"go.uber.org/multierr\"\n)\n\nfunc testLogger(t *testing.T) fxevent.Logger {\n\treturn fxlog.DefaultLogger(testutil.WriteSyncer{T: t})\n}\n\nfunc TestLifecycleStart(t *testing.T) {\n\tt.Parallel()\n\n\tt.Run(\"ExecutesInOrder\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tl := New(testLogger(t), fxclock.System)\n\t\tcount := 0\n\n\t\tl.Append(Hook{\n\t\t\tOnStart: func(context.Context) error {\n\t\t\t\tcount++\n\t\t\t\tassert.Equal(t, 1, count, \"expected this starter to be executed first\")\n\t\t\t\treturn nil\n\t\t\t},\n\t\t})\n\t\tl.Append(Hook{\n\t\t\tOnStart: func(context.Context) error {\n\t\t\t\tcount++\n\t\t\t\tassert.Equal(t, 2, count, \"expected this starter to be executed second\")\n\t\t\t\treturn nil\n\t\t\t},\n\t\t})\n\n\t\tassert.NoError(t, l.Start(context.Background()))\n\t\tassert.Equal(t, 2, count)\n\t})\n\n\tt.Run(\"ErrHaltsChainAndRollsBack\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tl := New(testLogger(t), fxclock.System)\n\t\terr := errors.New(\"a starter error\")\n\t\tstarterCount := 0\n\t\tstopperCount := 0\n\n\t\t// this event's starter succeeded, so no matter what the stopper should run\n\t\tl.Append(Hook{\n\t\t\tOnStart: func(context.Context) error {\n\t\t\t\tstarterCount++\n\t\t\t\treturn nil\n\t\t\t},\n\t\t\tOnStop: func(context.Context) error {\n\t\t\t\tstopperCount++\n\t\t\t\treturn nil\n\t\t\t},\n\t\t})\n\t\t// this event's starter fails, so the stopper shouldnt run\n\t\tl.Append(Hook{\n\t\t\tOnStart: func(context.Context) error {\n\t\t\t\tstarterCount++\n\t\t\t\treturn err\n\t\t\t},\n\t\t\tOnStop: func(context.Context) error {\n\t\t\t\tt.Error(\"this stopper shouldnt run, since the starter in this event failed\")\n\t\t\t\treturn nil\n\t\t\t},\n\t\t})\n\t\t// this event is last in the chain, so it should never run since the previous failed\n\t\tl.Append(Hook{\n\t\t\tOnStart: func(context.Context) error {\n\t\t\t\tt.Error(\"this starter should never run, since the previous event failed\")\n\t\t\t\treturn nil\n\t\t\t},\n\t\t\tOnStop: func(context.Context) error {\n\t\t\t\tt.Error(\"this stopper should never run, since the previous event failed\")\n\t\t\t\treturn nil\n\t\t\t},\n\t\t})\n\n\t\tassert.Error(t, err, l.Start(context.Background()))\n\t\tassert.NoError(t, l.Stop(context.Background()))\n\n\t\tassert.Equal(t, 2, starterCount, \"expected the first and second starter to execute\")\n\t\tassert.Equal(t, 1, stopperCount, \"expected the first stopper to execute since the second starter failed\")\n\t})\n\n\tt.Run(\"DoNotRunStartHooksWithExpiredCtx\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tl := New(testLogger(t), fxclock.System)\n\t\tl.Append(Hook{\n\t\t\tOnStart: func(context.Context) error {\n\t\t\t\tassert.Fail(t, \"this hook should not run\")\n\t\t\t\treturn nil\n\t\t\t},\n\t\t\tOnStop: func(context.Context) error {\n\t\t\t\tassert.Fail(t, \"this hook should not run\")\n\t\t\t\treturn nil\n\t\t\t},\n\t\t})\n\t\tctx, cancel := context.WithCancel(context.Background())\n\t\tcancel()\n\t\terr := l.Start(ctx)\n\t\trequire.Error(t, err)\n\t\t// Note: Stop does not return an error here because no hooks\n\t\t// have been started, so we don't end up any of the corresponding\n\t\t// stop hooks.\n\t\trequire.NoError(t, l.Stop(ctx))\n\t})\n\n\tt.Run(\"StartWhileStartedErrors\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tl := New(testLogger(t), fxclock.System)\n\t\tassert.NoError(t, l.Start(context.Background()))\n\t\terr := l.Start(context.Background())\n\t\trequire.Error(t, err)\n\t\tassert.Contains(t, err.Error(), \"attempted to start lifecycle when in state: started\")\n\t\tassert.NoError(t, l.Stop(context.Background()))\n\t\tassert.NoError(t, l.Start(context.Background()))\n\t})\n}\n\nfunc TestLifecycleStop(t *testing.T) {\n\tt.Parallel()\n\n\tt.Run(\"DoesNothingWithoutHooks\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tl := New(testLogger(t), fxclock.System)\n\t\tl.Start(context.Background())\n\t\tassert.Nil(t, l.Stop(context.Background()), \"no lifecycle hooks should have resulted in stop returning nil\")\n\t})\n\n\tt.Run(\"DoesNothingWhenNotStarted\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\thook := Hook{\n\t\t\tOnStop: func(context.Context) error {\n\t\t\t\tassert.Fail(t, \"OnStop should not be called if lifecycle was never started\")\n\t\t\t\treturn nil\n\t\t\t},\n\t\t}\n\t\tl := New(testLogger(t), fxclock.System)\n\t\tl.Append(hook)\n\t\tl.Stop(context.Background())\n\t})\n\n\tt.Run(\"ExecutesInReverseOrder\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tl := New(testLogger(t), fxclock.System)\n\t\tcount := 2\n\n\t\tl.Append(Hook{\n\t\t\tOnStop: func(context.Context) error {\n\t\t\t\tcount--\n\t\t\t\tassert.Equal(t, 0, count, \"this stopper was added first, so should execute last\")\n\t\t\t\treturn nil\n\t\t\t},\n\t\t})\n\t\tl.Append(Hook{\n\t\t\tOnStop: func(context.Context) error {\n\t\t\t\tcount--\n\t\t\t\tassert.Equal(t, 1, count, \"this stopper was added last, so should execute first\")\n\t\t\t\treturn nil\n\t\t\t},\n\t\t})\n\n\t\tassert.NoError(t, l.Start(context.Background()))\n\t\tassert.NoError(t, l.Stop(context.Background()))\n\t\tassert.Equal(t, 0, count)\n\t})\n\n\tt.Run(\"ErrDoesntHaltChain\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tl := New(testLogger(t), fxclock.System)\n\t\tcount := 0\n\n\t\tl.Append(Hook{\n\t\t\tOnStop: func(context.Context) error {\n\t\t\t\tcount++\n\t\t\t\treturn nil\n\t\t\t},\n\t\t})\n\t\terr := errors.New(\"some stop error\")\n\t\tl.Append(Hook{\n\t\t\tOnStop: func(context.Context) error {\n\t\t\t\tcount++\n\t\t\t\treturn err\n\t\t\t},\n\t\t})\n\n\t\tassert.NoError(t, l.Start(context.Background()))\n\t\tassert.Equal(t, err, l.Stop(context.Background()))\n\t\tassert.Equal(t, 2, count)\n\t})\n\tt.Run(\"GathersAllErrs\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tl := New(testLogger(t), fxclock.System)\n\n\t\terr := errors.New(\"some stop error\")\n\t\terr2 := errors.New(\"some other stop error\")\n\n\t\tl.Append(Hook{\n\t\t\tOnStop: func(context.Context) error {\n\t\t\t\treturn err2\n\t\t\t},\n\t\t})\n\t\tl.Append(Hook{\n\t\t\tOnStop: func(context.Context) error {\n\t\t\t\treturn err\n\t\t\t},\n\t\t})\n\n\t\tassert.NoError(t, l.Start(context.Background()))\n\t\tassert.Equal(t, multierr.Combine(err, err2), l.Stop(context.Background()))\n\t})\n\tt.Run(\"AllowEmptyHooks\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tl := New(testLogger(t), fxclock.System)\n\t\tl.Append(Hook{})\n\t\tl.Append(Hook{})\n\n\t\tassert.NoError(t, l.Start(context.Background()))\n\t\tassert.NoError(t, l.Stop(context.Background()))\n\t})\n\n\tt.Run(\"DoesNothingIfStartFailed\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tl := New(testLogger(t), fxclock.System)\n\t\terr := errors.New(\"some start error\")\n\n\t\tl.Append(Hook{\n\t\t\tOnStart: func(context.Context) error {\n\t\t\t\treturn err\n\t\t\t},\n\t\t\tOnStop: func(context.Context) error {\n\t\t\t\tassert.Fail(t, \"OnStop should not be called if start failed\")\n\t\t\t\treturn nil\n\t\t\t},\n\t\t})\n\n\t\tassert.Equal(t, err, l.Start(context.Background()))\n\t\tl.Stop(context.Background())\n\t})\n\n\tt.Run(\"DoNotRunStopHooksWithExpiredCtx\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tl := New(testLogger(t), fxclock.System)\n\t\tl.Append(Hook{\n\t\t\tOnStart: func(context.Context) error {\n\t\t\t\treturn nil\n\t\t\t},\n\t\t\tOnStop: func(context.Context) error {\n\t\t\t\tassert.Fail(t, \"this hook should not run\")\n\t\t\t\treturn nil\n\t\t\t},\n\t\t})\n\t\tctx, cancel := context.WithCancel(context.Background())\n\t\terr := l.Start(ctx)\n\t\trequire.NoError(t, err)\n\t\tcancel()\n\t\trequire.Error(t, l.Stop(ctx))\n\t})\n\n\tt.Run(\"nil ctx\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tl := New(testLogger(t), fxclock.System)\n\t\tl.Append(Hook{\n\t\t\tOnStart: func(context.Context) error {\n\t\t\t\tassert.Fail(t, \"this hook should not run\")\n\t\t\t\treturn nil\n\t\t\t},\n\t\t\tOnStop: func(context.Context) error {\n\t\t\t\tassert.Fail(t, \"this hook should not run\")\n\t\t\t\treturn nil\n\t\t\t},\n\t\t})\n\t\terr := l.Start(nil) //nolint:staticcheck // SA1012 this test specifically tests for the lint failure\n\t\trequire.Error(t, err)\n\t\tassert.Contains(t, err.Error(), \"called OnStart with nil context\")\n\n\t\terr = l.Stop(nil) //nolint:staticcheck // SA1012 this test specifically tests for the lint failure\n\t\trequire.Error(t, err)\n\t\tassert.Contains(t, err.Error(), \"called OnStop with nil context\")\n\t})\n}\n\nfunc TestHookRecordsFormat(t *testing.T) {\n\tt.Parallel()\n\n\tt.Run(\"SortRecords\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tt1, err := time.ParseDuration(\"10ms\")\n\t\trequire.NoError(t, err)\n\t\tt2, err := time.ParseDuration(\"20ms\")\n\t\trequire.NoError(t, err)\n\n\t\tf := fxreflect.Frame{\n\t\t\tFunction: \"someFunc\",\n\t\t\tFile:     \"somefunc.go\",\n\t\t\tLine:     1,\n\t\t}\n\n\t\tr := HookRecords{\n\t\t\tHookRecord{\n\t\t\t\tCallerFrame: f,\n\t\t\t\tFunc: func(context.Context) error {\n\t\t\t\t\treturn nil\n\t\t\t\t},\n\t\t\t\tRuntime: t1,\n\t\t\t},\n\t\t\tHookRecord{\n\t\t\t\tCallerFrame: f,\n\t\t\t\tFunc: func(context.Context) error {\n\t\t\t\t\treturn nil\n\t\t\t\t},\n\t\t\t\tRuntime: t2,\n\t\t\t},\n\t\t}\n\n\t\tsort.Sort(r)\n\t\tfor _, format := range []string{\"%v\", \"%+v\", \"%s\"} {\n\t\t\ts := fmt.Sprintf(format, r)\n\t\t\thook1Idx := strings.Index(s, \"TestHookRecordsFormat.func1.1()\")\n\t\t\thook2Idx := strings.Index(s, \"TestHookRecordsFormat.func1.2()\")\n\t\t\tassert.Greater(t, hook1Idx, hook2Idx, \"second hook must appear first in the formatted string\")\n\t\t\tassert.Contains(t, s, \"somefunc.go:1\", \"file name and line should be reported\")\n\t\t}\n\t})\n}\n\nfunc TestMain(m *testing.M) {\n\tgoleak.VerifyTestMain(m)\n}\n"
  },
  {
    "path": "internal/testutil/writer.go",
    "content": "// Copyright (c) 2020-2021 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage testutil\n\nimport (\n\t\"go.uber.org/zap/zapcore\"\n)\n\n// TestingT is a subset of the testing.T interface.\ntype TestingT interface {\n\tLogf(string, ...any)\n}\n\n// WriteSyncer is a zapcore.WriteSyncer that writes to the provided test\n// logger.\ntype WriteSyncer struct{ T TestingT }\n\nvar _ zapcore.WriteSyncer = WriteSyncer{}\n\n// Write writes the provided bytes to the underlying TestingT.\nfunc (w WriteSyncer) Write(bs []byte) (int, error) {\n\tw.T.Logf(\"%s\", bs)\n\treturn len(bs), nil\n}\n\n// Sync is a no-op.\nfunc (WriteSyncer) Sync() error { return nil }\n"
  },
  {
    "path": "internal/testutil/writer_test.go",
    "content": "// Copyright (c) 2020 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage testutil\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\n// spy is a TestingT that captures Log requests.\ntype spy struct{ Logs []string }\n\nfunc (s *spy) Clear() { s.Logs = nil }\n\nfunc (s *spy) Logf(msg string, args ...any) {\n\ts.Logs = append(s.Logs, fmt.Sprintf(msg, args...))\n}\n\nfunc TestWriteSyncer(t *testing.T) {\n\tt.Parallel()\n\n\tvar spy spy\n\tws := WriteSyncer{T: &spy}\n\n\tt.Run(\"log\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tspy.Clear()\n\n\t\tio.WriteString(ws, \"hello\")\n\t\tassert.Equal(t, []string{\"hello\"}, spy.Logs)\n\t})\n\n\tt.Run(\"sync\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tassert.NoError(t, ws.Sync())\n\t})\n}\n"
  },
  {
    "path": "invoke.go",
    "content": "// Copyright (c) 2019-2021 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage fx\n\nimport (\n\t\"fmt\"\n\t\"strings\"\n\n\t\"go.uber.org/fx/internal/fxreflect\"\n)\n\n// Invoke registers functions that are executed eagerly on application start.\n// Arguments for these invocations are built using the constructors registered\n// by Provide. Passing multiple Invoke options appends the new invocations to\n// the application's existing list.\n//\n// Unlike constructors, invocations are always executed, and they're always\n// run in order. Invocations may have any number of returned values.\n// If the final returned object is an error, it indicates whether the operation\n// was successful.\n// All other returned values are discarded.\n//\n// Invokes registered in [Module]s are run before the ones registered at the\n// scope of the parent. Invokes within the same Module is run in the order\n// they were provided. For example,\n//\n//\tfx.New(\n//\t\tfx.Invoke(func3),\n//\t\tfx.Module(\"someModule\",\n//\t\t\tfx.Invoke(func1),\n//\t\t\tfx.Invoke(func2),\n//\t\t),\n//\t\tfx.Invoke(func4),\n//\t)\n//\n// invokes func1, func2, func3, func4 in that order.\n//\n// Typically, invoked functions take a handful of high-level objects (whose\n// constructors depend on lower-level objects) and introduce them to each\n// other. This kick-starts the application by forcing it to instantiate a\n// variety of types.\n//\n// To see an invocation in use, read through the package-level example. For\n// advanced features, including optional parameters and named instances, see\n// the documentation of the In and Out types.\nfunc Invoke(funcs ...any) Option {\n\treturn invokeOption{\n\t\tTargets: funcs,\n\t\tStack:   fxreflect.CallerStack(1, 0),\n\t}\n}\n\ntype invokeOption struct {\n\tTargets []any\n\tStack   fxreflect.Stack\n}\n\nfunc (o invokeOption) apply(mod *module) {\n\tfor _, target := range o.Targets {\n\t\tmod.invokes = append(mod.invokes, invoke{\n\t\t\tTarget: target,\n\t\t\tStack:  o.Stack,\n\t\t})\n\t}\n}\n\nfunc (o invokeOption) String() string {\n\titems := make([]string, len(o.Targets))\n\tfor i, f := range o.Targets {\n\t\titems[i] = fxreflect.FuncName(f)\n\t}\n\treturn fmt.Sprintf(\"fx.Invoke(%s)\", strings.Join(items, \", \"))\n}\n\nfunc runInvoke(c container, i invoke) error {\n\tfn := i.Target\n\tswitch fn := fn.(type) {\n\tcase Option:\n\t\treturn fmt.Errorf(\"fx.Option should be passed to fx.New directly, \"+\n\t\t\t\"not to fx.Invoke: fx.Invoke received %v from:\\n%+v\",\n\t\t\tfn, i.Stack)\n\n\tcase annotated:\n\t\taf, err := fn.Build()\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\treturn c.Invoke(af)\n\tdefault:\n\t\treturn c.Invoke(fn)\n\t}\n}\n"
  },
  {
    "path": "lifecycle.go",
    "content": "// Copyright (c) 2022 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage fx\n\nimport (\n\t\"context\"\n\n\t\"go.uber.org/fx/internal/lifecycle\"\n)\n\n// A HookFunc is a function that can be used as a [Hook].\ntype HookFunc interface {\n\t~func() | ~func() error | ~func(context.Context) | ~func(context.Context) error\n}\n\n// Lifecycle allows constructors to register callbacks that are executed on\n// application start and stop. See the documentation for App for details on Fx\n// applications' initialization, startup, and shutdown logic.\ntype Lifecycle interface {\n\tAppend(Hook)\n}\n\n// A Hook is a pair of start and stop callbacks, either of which can be nil.\n// If a Hook's OnStart callback isn't executed (because a previous OnStart\n// failure short-circuited application startup), its OnStop callback won't be\n// executed.\ntype Hook struct {\n\tOnStart func(context.Context) error\n\tOnStop  func(context.Context) error\n\n\tonStartName string\n\tonStopName  string\n}\n\n// StartHook returns a new Hook with start as its [Hook.OnStart] function,\n// wrapping its signature as needed. For example, given the following function:\n//\n//\tfunc myfunc() {\n//\t  fmt.Println(\"hook called\")\n//\t}\n//\n// then calling:\n//\n//\tlifecycle.Append(StartHook(myfunc))\n//\n// is functionally equivalent to calling:\n//\n//\tlifecycle.Append(fx.Hook{\n//\t  OnStart: func(context.Context) error {\n//\t    myfunc()\n//\t    return nil\n//\t  },\n//\t})\n//\n// The same is true for all functions that satisfy the HookFunc constraint.\n// Note that any context.Context parameter or error return will be propagated\n// as expected. If propagation is not intended, users should instead provide a\n// closure that discards the undesired value(s), or construct a Hook directly.\nfunc StartHook[T HookFunc](start T) Hook {\n\tonstart, startname := lifecycle.Wrap(start)\n\n\treturn Hook{\n\t\tOnStart:     onstart,\n\t\tonStartName: startname,\n\t}\n}\n\n// StopHook returns a new Hook with stop as its [Hook.OnStop] function,\n// wrapping its signature as needed. For example, given the following function:\n//\n//\tfunc myfunc() {\n//\t  fmt.Println(\"hook called\")\n//\t}\n//\n// then calling:\n//\n//\tlifecycle.Append(StopHook(myfunc))\n//\n// is functionally equivalent to calling:\n//\n//\tlifecycle.Append(fx.Hook{\n//\t  OnStop: func(context.Context) error {\n//\t    myfunc()\n//\t    return nil\n//\t  },\n//\t})\n//\n// The same is true for all functions that satisfy the HookFunc constraint.\n// Note that any context.Context parameter or error return will be propagated\n// as expected. If propagation is not intended, users should instead provide a\n// closure that discards the undesired value(s), or construct a Hook directly.\nfunc StopHook[T HookFunc](stop T) Hook {\n\tonstop, stopname := lifecycle.Wrap(stop)\n\n\treturn Hook{\n\t\tOnStop:     onstop,\n\t\tonStopName: stopname,\n\t}\n}\n\n// StartStopHook returns a new Hook with start as its [Hook.OnStart] function\n// and stop as its [Hook.OnStop] function, independently wrapping the signature\n// of each as needed.\nfunc StartStopHook[T1 HookFunc, T2 HookFunc](start T1, stop T2) Hook {\n\tvar (\n\t\tonstart, startname = lifecycle.Wrap(start)\n\t\tonstop, stopname   = lifecycle.Wrap(stop)\n\t)\n\n\treturn Hook{\n\t\tOnStart:     onstart,\n\t\tOnStop:      onstop,\n\t\tonStartName: startname,\n\t\tonStopName:  stopname,\n\t}\n}\n\ntype lifecycleWrapper struct {\n\t*lifecycle.Lifecycle\n}\n\nfunc (l *lifecycleWrapper) Append(h Hook) {\n\tl.Lifecycle.Append(lifecycle.Hook{\n\t\tOnStart:     h.OnStart,\n\t\tOnStop:      h.OnStop,\n\t\tOnStartName: h.onStartName,\n\t\tOnStopName:  h.onStopName,\n\t})\n}\n"
  },
  {
    "path": "log.go",
    "content": "// Copyright (c) 2021 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage fx\n\nimport (\n\t\"go.uber.org/fx/fxevent\"\n)\n\n// logBuffer will buffer all messages until a logger has been\n// initialized.\ntype logBuffer struct {\n\tevents []fxevent.Event\n\tlogger fxevent.Logger\n}\n\n// LogEvent buffers or logs an event.\nfunc (l *logBuffer) LogEvent(event fxevent.Event) {\n\tif l.logger == nil {\n\t\tl.events = append(l.events, event)\n\t} else {\n\t\tl.logger.LogEvent(event)\n\t}\n}\n\n// Connect flushes out all buffered events to a logger and resets them.\nfunc (l *logBuffer) Connect(logger fxevent.Logger) {\n\tl.logger = logger\n\tfor _, e := range l.events {\n\t\tlogger.LogEvent(e)\n\t}\n\tl.events = nil\n}\n"
  },
  {
    "path": "log_test.go",
    "content": "// Copyright (c) 2021 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage fx\n\nimport (\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"go.uber.org/fx/fxevent\"\n\t\"go.uber.org/fx/internal/fxlog\"\n\t\"go.uber.org/zap\"\n\t\"go.uber.org/zap/zaptest\"\n\t\"go.uber.org/zap/zaptest/observer\"\n)\n\nfunc TestLogBufferConnect(t *testing.T) {\n\tt.Parallel()\n\n\tspy := new(fxlog.Spy)\n\tevent := &fxevent.Started{}\n\tlb := &logBuffer{\n\t\tevents: []fxevent.Event{event},\n\t\tlogger: nil,\n\t}\n\n\tlb.Connect(spy)\n\tassert.Equal(t, fxlog.Events{event}, spy.Events())\n}\n\nfunc TestLogBufferLog(t *testing.T) {\n\tt.Parallel()\n\n\tspy := new(fxlog.Spy)\n\tevent := &fxevent.Started{}\n\tlb := &logBuffer{\n\t\tevents: nil,\n\t\tlogger: nil,\n\t}\n\n\tlb.LogEvent(event)\n\n\tlb.Connect(spy)\n\tassert.Equal(t, fxlog.Events{event}, spy.Events())\n}\n\nfunc TestWithLoggerDecorate(t *testing.T) {\n\tt.Parallel()\n\n\tcore, logs := observer.New(zap.DebugLevel)\n\tNew(\n\t\tSupply(zaptest.NewLogger(t)), // provide a logger\n\t\tReplace(zap.New(core)),       // and replace it\n\t\tWithLogger(func(log *zap.Logger) fxevent.Logger {\n\t\t\treturn &fxevent.ZapLogger{Logger: log}\n\t\t}),\n\t)\n\n\tassert.NotZero(t, logs.Len(),\n\t\t\"should post to replacement logger\")\n}\n"
  },
  {
    "path": "module.go",
    "content": "// Copyright (c) 2022 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage fx\n\nimport (\n\t\"fmt\"\n\n\t\"go.uber.org/dig\"\n\t\"go.uber.org/fx/fxevent\"\n\t\"go.uber.org/fx/internal/fxreflect\"\n\t\"go.uber.org/multierr\"\n)\n\n// A container represents a set of constructors to provide\n// dependencies, and a set of functions to invoke once all the\n// dependencies have been initialized.\n//\n// This definition corresponds to the dig.Container and dig.Scope.\ntype container interface {\n\tInvoke(any, ...dig.InvokeOption) error\n\tProvide(any, ...dig.ProvideOption) error\n\tDecorate(any, ...dig.DecorateOption) error\n}\n\n// Module is a named group of zero or more fx.Options.\n//\n// A Module scopes the effect of certain operations to within the module.\n// For more information, see [Decorate], [Replace], or [Invoke].\n//\n// Module allows packages to bundle sophisticated functionality into easy-to-use\n// logical units.\n// For example, a logging package might export a simple option like this:\n//\n//\tpackage logging\n//\n//\tvar Module = fx.Module(\"logging\",\n//\t\tfx.Provide(func() *log.Logger {\n//\t\t\treturn log.New(os.Stdout, \"\", 0)\n//\t\t}),\n//\t\t// ...\n//\t)\n//\n// A shared all-in-one microservice package could use Module to bundle\n// all required components of a microservice:\n//\n//\tpackage server\n//\n//\tvar Module = fx.Module(\"server\",\n//\t\tlogging.Module,\n//\t\tmetrics.Module,\n//\t\ttracing.Module,\n//\t\trpc.Module,\n//\t)\n//\n// When new global functionality is added to the service ecosystem,\n// it can be added to the shared module with minimal churn for users.\n//\n// Use the all-in-one pattern sparingly.\n// It limits the flexibility available to the application.\nfunc Module(name string, opts ...Option) Option {\n\tmo := moduleOption{\n\t\tname:     name,\n\t\tlocation: fxreflect.CallerStack(1, 2)[0],\n\t\toptions:  opts,\n\t}\n\treturn mo\n}\n\ntype moduleOption struct {\n\tname     string\n\tlocation fxreflect.Frame\n\toptions  []Option\n}\n\nfunc (o moduleOption) String() string {\n\treturn fmt.Sprintf(\"fx.Module(%q, %v)\", o.name, o.options)\n}\n\nfunc (o moduleOption) apply(mod *module) {\n\t// This get called on any submodules' that are declared\n\t// as part of another module.\n\n\t// 1. Create a new module with the parent being the specified\n\t// module.\n\t// 2. Apply child Options on the new module.\n\t// 3. Append it to the parent module.\n\n\t// Create trace as parent's trace with this module's location pre-pended.\n\ttrace := append([]string{fmt.Sprintf(\"%v (%v)\", o.location, o.name)}, mod.trace...)\n\tnewModule := &module{\n\t\tname:   o.name,\n\t\tparent: mod,\n\t\ttrace:  trace,\n\t\tapp:    mod.app,\n\t}\n\tfor _, opt := range o.options {\n\t\topt.apply(newModule)\n\t}\n\tmod.modules = append(mod.modules, newModule)\n}\n\ntype module struct {\n\tparent         *module\n\tname           string\n\ttrace          []string\n\tscope          scope\n\tprovides       []provide\n\tinvokes        []invoke\n\tdecorators     []decorator\n\tmodules        []*module\n\tapp            *App\n\tlog            fxevent.Logger\n\tfallbackLogger fxevent.Logger\n\tlogConstructor *provide\n}\n\n// scope is a private wrapper interface for dig.Container and dig.Scope.\n// We can consider moving this into Fx using type constraints after Go 1.20\n// is released and 1.17 is deprecated.\ntype scope interface {\n\tDecorate(f any, opts ...dig.DecorateOption) error\n\tInvoke(f any, opts ...dig.InvokeOption) error\n\tProvide(f any, opts ...dig.ProvideOption) error\n\tScope(name string, opts ...dig.ScopeOption) *dig.Scope\n\tString() string\n}\n\n// builds the Scopes using the App's Container. Note that this happens\n// after applyModules' are called because the App's Container needs to\n// be built for any Scopes to be initialized, and applys' should be called\n// before the Container can get initialized.\nfunc (m *module) build(app *App, root *dig.Container) {\n\tif m.parent == nil {\n\t\tm.scope = root\n\t} else {\n\t\tparentScope := m.parent.scope\n\t\tm.scope = parentScope.Scope(m.name)\n\t\t// use parent module's logger by default\n\t\tm.log = m.parent.log\n\t}\n\n\tif m.logConstructor != nil {\n\t\t// Since user supplied a custom logger, use a buffered logger\n\t\t// to hold all messages until user supplied logger is\n\t\t// instantiated. Then we flush those messages after fully\n\t\t// constructing the custom logger.\n\t\tm.fallbackLogger, m.log = m.log, new(logBuffer)\n\t}\n\n\tfor _, mod := range m.modules {\n\t\tmod.build(app, root)\n\t}\n}\n\nfunc (m *module) provideAll() {\n\tfor _, p := range m.provides {\n\t\tm.provide(p)\n\t}\n\n\tfor _, m := range m.modules {\n\t\tm.provideAll()\n\t}\n}\n\nfunc (m *module) provide(p provide) {\n\tif m.app.err != nil {\n\t\treturn\n\t}\n\n\tif p.IsSupply {\n\t\tm.supply(p)\n\t\treturn\n\t}\n\n\tfuncName := fxreflect.FuncName(p.Target)\n\tvar info dig.ProvideInfo\n\topts := []dig.ProvideOption{\n\t\tdig.FillProvideInfo(&info),\n\t\tdig.Export(!p.Private),\n\t\tdig.WithProviderBeforeCallback(func(bci dig.BeforeCallbackInfo) {\n\t\t\tm.log.LogEvent(&fxevent.BeforeRun{\n\t\t\t\tName:       funcName,\n\t\t\t\tKind:       \"provide\",\n\t\t\t\tModuleName: m.name,\n\t\t\t})\n\t\t}),\n\t\tdig.WithProviderCallback(func(ci dig.CallbackInfo) {\n\t\t\tm.log.LogEvent(&fxevent.Run{\n\t\t\t\tName:       funcName,\n\t\t\t\tKind:       \"provide\",\n\t\t\t\tModuleName: m.name,\n\t\t\t\tRuntime:    ci.Runtime,\n\t\t\t\tErr:        ci.Error,\n\t\t\t})\n\t\t}),\n\t}\n\n\tif err := runProvide(m.scope, p, opts...); err != nil {\n\t\tm.app.err = err\n\t}\n\toutputNames := make([]string, len(info.Outputs))\n\tfor i, o := range info.Outputs {\n\t\toutputNames[i] = o.String()\n\t}\n\n\tm.log.LogEvent(&fxevent.Provided{\n\t\tConstructorName: funcName,\n\t\tStackTrace:      p.Stack.Strings(),\n\t\tModuleTrace:     append([]string{p.Stack[0].String()}, m.trace...),\n\t\tModuleName:      m.name,\n\t\tOutputTypeNames: outputNames,\n\t\tErr:             m.app.err,\n\t\tPrivate:         p.Private,\n\t})\n}\n\nfunc (m *module) supply(p provide) {\n\ttypeName := p.SupplyType.String()\n\topts := []dig.ProvideOption{\n\t\tdig.Export(!p.Private),\n\t\tdig.WithProviderBeforeCallback(func(bci dig.BeforeCallbackInfo) {\n\t\t\tm.log.LogEvent(&fxevent.BeforeRun{\n\t\t\t\tName:       fmt.Sprintf(\"stub(%v)\", typeName),\n\t\t\t\tKind:       \"supply\",\n\t\t\t\tModuleName: m.name,\n\t\t\t})\n\t\t}),\n\t\tdig.WithProviderCallback(func(ci dig.CallbackInfo) {\n\t\t\tm.log.LogEvent(&fxevent.Run{\n\t\t\t\tName:       fmt.Sprintf(\"stub(%v)\", typeName),\n\t\t\t\tKind:       \"supply\",\n\t\t\t\tRuntime:    ci.Runtime,\n\t\t\t\tModuleName: m.name,\n\t\t\t})\n\t\t}),\n\t}\n\n\tif err := runProvide(m.scope, p, opts...); err != nil {\n\t\tm.app.err = err\n\t}\n\n\tm.log.LogEvent(&fxevent.Supplied{\n\t\tTypeName:    typeName,\n\t\tStackTrace:  p.Stack.Strings(),\n\t\tModuleTrace: append([]string{p.Stack[0].String()}, m.trace...),\n\t\tModuleName:  m.name,\n\t\tErr:         m.app.err,\n\t})\n}\n\n// Constructs custom loggers for all modules in the tree\nfunc (m *module) installAllEventLoggers() {\n\tif m.logConstructor != nil {\n\t\tif buffer, ok := m.log.(*logBuffer); ok {\n\t\t\t// default to parent's logger if custom logger constructor fails\n\t\t\tif err := m.installEventLogger(buffer); err != nil {\n\t\t\t\tm.app.err = multierr.Append(m.app.err, err)\n\t\t\t\tm.log = m.fallbackLogger\n\t\t\t\tbuffer.Connect(m.log)\n\t\t\t}\n\t\t}\n\t\tm.fallbackLogger = nil\n\t} else if m.parent != nil {\n\t\tm.log = m.parent.log\n\t}\n\n\tfor _, mod := range m.modules {\n\t\tmod.installAllEventLoggers()\n\t}\n}\n\nfunc (m *module) installEventLogger(buffer *logBuffer) (err error) {\n\tp := m.logConstructor\n\tfname := fxreflect.FuncName(p.Target)\n\tdefer func() {\n\t\tm.log.LogEvent(&fxevent.LoggerInitialized{\n\t\t\tErr:             err,\n\t\t\tConstructorName: fname,\n\t\t})\n\t}()\n\n\t// TODO: Use dig.FillProvideInfo to inspect the provided constructor\n\t// and fail the application if its signature didn't match.\n\tif err := m.scope.Provide(p.Target); err != nil {\n\t\treturn fmt.Errorf(\"fx.WithLogger(%v) from:\\n%+v\\nin Module: %q\\nFailed: %w\",\n\t\t\tfname, p.Stack, m.name, err)\n\t}\n\n\treturn m.scope.Invoke(func(log fxevent.Logger) {\n\t\tm.log = log\n\t\tbuffer.Connect(log)\n\t})\n}\n\nfunc (m *module) invokeAll() error {\n\tfor _, m := range m.modules {\n\t\tif err := m.invokeAll(); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\tfor _, invoke := range m.invokes {\n\t\tif err := m.invoke(invoke); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\treturn nil\n}\n\nfunc (m *module) invoke(i invoke) (err error) {\n\tfnName := fxreflect.FuncName(i.Target)\n\tm.log.LogEvent(&fxevent.Invoking{\n\t\tFunctionName: fnName,\n\t\tModuleName:   m.name,\n\t})\n\terr = runInvoke(m.scope, i)\n\tm.log.LogEvent(&fxevent.Invoked{\n\t\tFunctionName: fnName,\n\t\tModuleName:   m.name,\n\t\tErr:          err,\n\t\tTrace:        fmt.Sprintf(\"%+v\", i.Stack), // format stack trace as multi-line\n\t})\n\treturn err\n}\n\nfunc (m *module) decorateAll() error {\n\tfor _, d := range m.decorators {\n\t\tif err := m.decorate(d); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\tfor _, m := range m.modules {\n\t\tif err := m.decorateAll(); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc (m *module) decorate(d decorator) (err error) {\n\tif d.IsReplace {\n\t\treturn m.replace(d)\n\t}\n\n\tfuncName := fxreflect.FuncName(d.Target)\n\tvar info dig.DecorateInfo\n\topts := []dig.DecorateOption{\n\t\tdig.FillDecorateInfo(&info),\n\t\tdig.WithDecoratorBeforeCallback(func(bci dig.BeforeCallbackInfo) {\n\t\t\tm.log.LogEvent(&fxevent.BeforeRun{\n\t\t\t\tName:       funcName,\n\t\t\t\tKind:       \"decorate\",\n\t\t\t\tModuleName: m.name,\n\t\t\t})\n\t\t}),\n\t\tdig.WithDecoratorCallback(func(ci dig.CallbackInfo) {\n\t\t\tm.log.LogEvent(&fxevent.Run{\n\t\t\t\tName:       funcName,\n\t\t\t\tKind:       \"decorate\",\n\t\t\t\tModuleName: m.name,\n\t\t\t\tRuntime:    ci.Runtime,\n\t\t\t\tErr:        ci.Error,\n\t\t\t})\n\t\t}),\n\t}\n\n\terr = runDecorator(m.scope, d, opts...)\n\toutputNames := make([]string, len(info.Outputs))\n\tfor i, o := range info.Outputs {\n\t\toutputNames[i] = o.String()\n\t}\n\n\tm.log.LogEvent(&fxevent.Decorated{\n\t\tDecoratorName:   funcName,\n\t\tStackTrace:      d.Stack.Strings(),\n\t\tModuleTrace:     append([]string{d.Stack[0].String()}, m.trace...),\n\t\tModuleName:      m.name,\n\t\tOutputTypeNames: outputNames,\n\t\tErr:             err,\n\t})\n\n\treturn err\n}\n\nfunc (m *module) replace(d decorator) error {\n\ttypeName := d.ReplaceType.String()\n\topts := []dig.DecorateOption{\n\t\tdig.WithDecoratorBeforeCallback(func(bci dig.BeforeCallbackInfo) {\n\t\t\tm.log.LogEvent(&fxevent.BeforeRun{\n\t\t\t\tName:       fmt.Sprintf(\"stub(%v)\", typeName),\n\t\t\t\tKind:       \"replace\",\n\t\t\t\tModuleName: m.name,\n\t\t\t})\n\t\t}),\n\t\tdig.WithDecoratorCallback(func(ci dig.CallbackInfo) {\n\t\t\tm.log.LogEvent(&fxevent.Run{\n\t\t\t\tName:       fmt.Sprintf(\"stub(%v)\", typeName),\n\t\t\t\tKind:       \"replace\",\n\t\t\t\tModuleName: m.name,\n\t\t\t\tRuntime:    ci.Runtime,\n\t\t\t\tErr:        ci.Error,\n\t\t\t})\n\t\t}),\n\t}\n\n\terr := runDecorator(m.scope, d, opts...)\n\tm.log.LogEvent(&fxevent.Replaced{\n\t\tModuleName:      m.name,\n\t\tStackTrace:      d.Stack.Strings(),\n\t\tModuleTrace:     append([]string{d.Stack[0].String()}, m.trace...),\n\t\tOutputTypeNames: []string{typeName},\n\t\tErr:             err,\n\t})\n\treturn err\n}\n"
  },
  {
    "path": "module_test.go",
    "content": "// Copyright (c) 2022 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage fx_test\n\nimport (\n\t\"bytes\"\n\t\"errors\"\n\t\"log\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\t\"go.uber.org/fx\"\n\t\"go.uber.org/fx/fxevent\"\n\t\"go.uber.org/fx/fxtest\"\n\t\"go.uber.org/fx/internal/fxlog\"\n\t\"go.uber.org/zap\"\n)\n\nfunc TestModuleSuccess(t *testing.T) {\n\tt.Parallel()\n\n\ttype Logger struct {\n\t\tName string\n\t}\n\n\ttype Foo struct {\n\t\tName string\n\t}\n\n\tt.Run(\"provide a dependency from a submodule\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tredis := fx.Module(\"redis\",\n\t\t\tfx.Provide(func() *Logger {\n\t\t\t\treturn &Logger{Name: \"redis\"}\n\t\t\t}),\n\t\t)\n\n\t\tapp := fxtest.New(t,\n\t\t\tredis,\n\t\t\tfx.Invoke(func(l *Logger) {\n\t\t\t\tassert.Equal(t, \"redis\", l.Name)\n\t\t\t}),\n\t\t)\n\n\t\tdefer app.RequireStart().RequireStop()\n\t})\n\n\tt.Run(\"provide a dependency from nested modules\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tapp := fxtest.New(t,\n\t\t\tfx.Module(\"child\",\n\t\t\t\tfx.Module(\"grandchild\",\n\t\t\t\t\tfx.Provide(func() *Logger {\n\t\t\t\t\t\treturn &Logger{Name: \"redis\"}\n\t\t\t\t\t}),\n\t\t\t\t),\n\t\t\t),\n\t\t\tfx.Invoke(func(l *Logger) {\n\t\t\t\tassert.Equal(t, \"redis\", l.Name)\n\t\t\t}),\n\t\t)\n\t\tdefer app.RequireStart().RequireStop()\n\t})\n\n\tt.Run(\"provide a value to a soft group value from nested modules\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\ttype Param struct {\n\t\t\tfx.In\n\n\t\t\tFoos []string `group:\"foo,soft\"`\n\t\t\tBar  int\n\t\t}\n\t\ttype Result struct {\n\t\t\tfx.Out\n\n\t\t\tFoo string `group:\"foo\"`\n\t\t\tBar int\n\t\t}\n\t\tapp := fxtest.New(t,\n\t\t\tfx.Module(\"child\",\n\t\t\t\tfx.Module(\"grandchild\",\n\t\t\t\t\tfx.Provide(fx.Annotate(\n\t\t\t\t\t\tfunc() string {\n\t\t\t\t\t\t\trequire.FailNow(t, \"should not be called\")\n\t\t\t\t\t\t\treturn \"there\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\tfx.ResultTags(`group:\"foo\"`),\n\t\t\t\t\t)),\n\t\t\t\t\tfx.Provide(func() Result {\n\t\t\t\t\t\treturn Result{Foo: \"hello\", Bar: 10}\n\t\t\t\t\t}),\n\t\t\t\t),\n\t\t\t),\n\t\t\tfx.Invoke(func(p Param) {\n\t\t\t\tassert.ElementsMatch(t, []string{\"hello\"}, p.Foos)\n\t\t\t}),\n\t\t)\n\t\tdefer app.RequireStart().RequireStop()\n\t\trequire.NoError(t, app.Err())\n\t})\n\n\tt.Run(\"invoke from nested module\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tinvokeRan := false\n\t\tapp := fxtest.New(t,\n\t\t\tfx.Provide(func() *Logger {\n\t\t\t\treturn &Logger{\n\t\t\t\t\tName: \"redis\",\n\t\t\t\t}\n\t\t\t}),\n\t\t\tfx.Module(\"child\",\n\t\t\t\tfx.Module(\"grandchild\",\n\t\t\t\t\tfx.Invoke(func(l *Logger) {\n\t\t\t\t\t\tassert.Equal(t, \"redis\", l.Name)\n\t\t\t\t\t\tinvokeRan = true\n\t\t\t\t\t}),\n\t\t\t\t),\n\t\t\t),\n\t\t)\n\t\trequire.True(t, invokeRan)\n\t\trequire.NoError(t, app.Err())\n\t\tdefer app.RequireStart().RequireStop()\n\t})\n\n\tt.Run(\"invoke in module with dep from top module\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tchild := fx.Module(\"child\",\n\t\t\tfx.Invoke(func(l *Logger) {\n\t\t\t\tassert.Equal(t, \"my logger\", l.Name)\n\t\t\t}),\n\t\t)\n\t\tapp := fxtest.New(t,\n\t\t\tchild,\n\t\t\tfx.Provide(func() *Logger {\n\t\t\t\treturn &Logger{Name: \"my logger\"}\n\t\t\t}),\n\t\t)\n\t\tdefer app.RequireStart().RequireStop()\n\t})\n\n\tt.Run(\"provide in module with annotate\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tchild := fx.Module(\"child\",\n\t\t\tfx.Provide(fx.Annotate(func() *Logger {\n\t\t\t\treturn &Logger{Name: \"good logger\"}\n\t\t\t}, fx.ResultTags(`name:\"goodLogger\"`))),\n\t\t)\n\t\tapp := fxtest.New(t,\n\t\t\tchild,\n\t\t\tfx.Invoke(fx.Annotate(func(l *Logger) {\n\t\t\t\tassert.Equal(t, \"good logger\", l.Name)\n\t\t\t}, fx.ParamTags(`name:\"goodLogger\"`))),\n\t\t)\n\t\tdefer app.RequireStart().RequireStop()\n\t})\n\n\tt.Run(\"invoke in module with annotate\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tranInvoke := false\n\t\tchild := fx.Module(\"child\",\n\t\t\t// use something provided by the root module.\n\t\t\tfx.Invoke(fx.Annotate(func(l *Logger) {\n\t\t\t\tassert.Equal(t, \"good logger\", l.Name)\n\t\t\t\tranInvoke = true\n\t\t\t})),\n\t\t)\n\t\tapp := fxtest.New(t,\n\t\t\tchild,\n\t\t\tfx.Provide(fx.Annotate(func() *Logger {\n\t\t\t\treturn &Logger{Name: \"good logger\"}\n\t\t\t})),\n\t\t)\n\t\tdefer app.RequireStart().RequireStop()\n\t\tassert.True(t, ranInvoke)\n\t})\n\n\tt.Run(\"use Options in Module\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\topts := fx.Options(\n\t\t\tfx.Provide(fx.Annotate(func() string {\n\t\t\t\treturn \"dog\"\n\t\t\t}, fx.ResultTags(`group:\"pets\"`))),\n\t\t\tfx.Provide(fx.Annotate(func() string {\n\t\t\t\treturn \"cat\"\n\t\t\t}, fx.ResultTags(`group:\"pets\"`))),\n\t\t)\n\n\t\tpetModule := fx.Module(\"pets\", opts)\n\n\t\tapp := fxtest.New(t,\n\t\t\tpetModule,\n\t\t\tfx.Invoke(fx.Annotate(func(pets []string) {\n\t\t\t\tassert.ElementsMatch(t, []string{\"dog\", \"cat\"}, pets)\n\t\t\t}, fx.ParamTags(`group:\"pets\"`))),\n\t\t)\n\n\t\tdefer app.RequireStart().RequireStop()\n\t})\n\n\tt.Run(\"Invoke order in Modules\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\ttype person struct {\n\t\t\tage int\n\t\t}\n\n\t\tapp := fxtest.New(t,\n\t\t\tfx.Provide(func() *person {\n\t\t\t\treturn &person{\n\t\t\t\t\tage: 1,\n\t\t\t\t}\n\t\t\t}),\n\t\t\tfx.Invoke(func(p *person) {\n\t\t\t\tassert.Equal(t, 2, p.age)\n\t\t\t\tp.age++\n\t\t\t}),\n\t\t\tfx.Module(\"module\",\n\t\t\t\tfx.Invoke(func(p *person) {\n\t\t\t\t\tassert.Equal(t, 1, p.age)\n\t\t\t\t\tp.age++\n\t\t\t\t}),\n\t\t\t),\n\t\t\tfx.Invoke(func(p *person) {\n\t\t\t\tassert.Equal(t, 3, p.age)\n\t\t\t}),\n\t\t)\n\t\trequire.NoError(t, app.Err())\n\t})\n\n\tt.Run(\"custom logger for module\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\ttests := []struct {\n\t\t\tdesc           string\n\t\t\tgiveWithLogger fx.Option\n\t\t\twantEvents     []string\n\t\t}{\n\t\t\t{\n\t\t\t\tdesc:           \"custom logger for module\",\n\t\t\t\tgiveWithLogger: fx.NopLogger,\n\t\t\t\twantEvents: []string{\n\t\t\t\t\t\"Provided\", \"Provided\", \"Provided\", \"Supplied\",\n\t\t\t\t\t\"BeforeRun\", \"Run\", \"LoggerInitialized\", \"Invoking\", \"Invoked\",\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tdesc:           \"Not using a custom logger for module defaults to app logger\",\n\t\t\t\tgiveWithLogger: fx.Options(),\n\t\t\t\twantEvents: []string{\n\t\t\t\t\t\"Provided\", \"Provided\", \"Provided\", \"Supplied\", \"Provided\", \"BeforeRun\", \"Run\",\n\t\t\t\t\t\"LoggerInitialized\", \"Invoking\", \"BeforeRun\", \"Run\", \"Invoked\", \"Invoking\", \"Invoked\",\n\t\t\t\t},\n\t\t\t},\n\t\t}\n\n\t\tfor _, tt := range tests {\n\t\t\tt.Run(tt.desc, func(t *testing.T) {\n\t\t\t\tt.Parallel()\n\t\t\t\tvar spy fxlog.Spy\n\n\t\t\t\tredis := fx.Module(\"redis\",\n\t\t\t\t\tfx.Provide(func() *Foo {\n\t\t\t\t\t\treturn &Foo{Name: \"redis\"}\n\t\t\t\t\t}),\n\t\t\t\t\tfx.Invoke(func(r *Foo) {\n\t\t\t\t\t\tassert.Equal(t, \"redis\", r.Name)\n\t\t\t\t\t}),\n\t\t\t\t\ttt.giveWithLogger,\n\t\t\t\t)\n\n\t\t\t\tapp := fxtest.New(t,\n\t\t\t\t\tredis,\n\t\t\t\t\tfx.Supply(&spy),\n\t\t\t\t\tfx.WithLogger(func(spy *fxlog.Spy) fxevent.Logger {\n\t\t\t\t\t\treturn spy\n\t\t\t\t\t}),\n\t\t\t\t\tfx.Invoke(func(r *Foo) {\n\t\t\t\t\t\tassert.Equal(t, \"redis\", r.Name)\n\t\t\t\t\t}),\n\t\t\t\t)\n\n\t\t\t\t// events from module with a custom logger not logged in app logger\n\t\t\t\tassert.Equal(t, tt.wantEvents, spy.EventTypes())\n\n\t\t\t\tspy.Reset()\n\t\t\t\tapp.RequireStart().RequireStop()\n\n\t\t\t\trequire.NoError(t, app.Err())\n\n\t\t\t\tassert.Equal(t, []string{\"Started\", \"Stopped\"}, spy.EventTypes())\n\t\t\t})\n\t\t}\n\t})\n\n\ttype NamedSpy struct {\n\t\tfxlog.Spy\n\t\tname string\n\t}\n\n\tt.Run(\"decorator on module logger does not affect app logger\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tappSpy := NamedSpy{name: \"app\"}\n\t\tmoduleSpy := NamedSpy{name: \"redis\"}\n\n\t\tredis := fx.Module(\"redis\",\n\t\t\tfx.Provide(func() *Foo {\n\t\t\t\treturn &Foo{Name: \"redis\"}\n\t\t\t}),\n\t\t\tfx.Supply(&appSpy),\n\t\t\tfx.Replace(&moduleSpy),\n\t\t\tfx.WithLogger(func(spy *NamedSpy) fxevent.Logger {\n\t\t\t\tassert.Equal(t, \"redis\", spy.name)\n\t\t\t\treturn spy\n\t\t\t}),\n\t\t\tfx.Invoke(func(r *Foo) {\n\t\t\t\tassert.Equal(t, \"redis\", r.Name)\n\t\t\t}),\n\t\t)\n\n\t\tapp := fxtest.New(t,\n\t\t\tredis,\n\t\t\tfx.WithLogger(func(spy *NamedSpy) fxevent.Logger {\n\t\t\t\tassert.Equal(t, \"app\", spy.name)\n\t\t\t\treturn spy\n\t\t\t}),\n\t\t\tfx.Invoke(func(r *Foo) {\n\t\t\t\tassert.Equal(t, \"redis\", r.Name)\n\t\t\t}),\n\t\t)\n\n\t\tassert.Equal(t, []string{\n\t\t\t\"Provided\", \"Supplied\", \"Replaced\", \"BeforeRun\", \"Run\", \"BeforeRun\", \"Run\",\n\t\t\t\"LoggerInitialized\", \"Invoking\", \"BeforeRun\", \"Run\", \"Invoked\",\n\t\t}, moduleSpy.EventTypes())\n\n\t\tassert.Equal(t, []string{\n\t\t\t\"Provided\", \"Provided\", \"Provided\",\n\t\t\t\"LoggerInitialized\", \"Invoking\", \"Invoked\",\n\t\t}, appSpy.EventTypes())\n\n\t\tappSpy.Reset()\n\t\tmoduleSpy.Reset()\n\n\t\tapp.RequireStart().RequireStop()\n\n\t\trequire.NoError(t, app.Err())\n\n\t\tassert.Equal(t, []string{\"Started\", \"Stopped\"}, appSpy.EventTypes())\n\t\tassert.Empty(t, moduleSpy.EventTypes())\n\t})\n\n\tt.Run(\"module uses parent module's logger to log events\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tappSpy := NamedSpy{name: \"app\"}\n\t\tchildSpy := NamedSpy{name: \"child\"}\n\n\t\tgrandchild := fx.Module(\"grandchild\",\n\t\t\tfx.Provide(func() *Foo {\n\t\t\t\treturn &Foo{Name: \"grandchild\"}\n\t\t\t}),\n\t\t\tfx.Invoke(func(r *Foo) {\n\t\t\t\tassert.Equal(t, \"grandchild\", r.Name)\n\t\t\t}),\n\t\t)\n\n\t\tchild := fx.Module(\"child\",\n\t\t\tgrandchild,\n\t\t\tfx.Supply(&appSpy),\n\t\t\tfx.Replace(&childSpy),\n\t\t\tfx.WithLogger(func(spy *NamedSpy) fxevent.Logger {\n\t\t\t\tassert.Equal(t, \"child\", spy.name)\n\t\t\t\treturn spy\n\t\t\t}),\n\t\t\tfx.Invoke(func(r *Foo) {\n\t\t\t\tassert.Equal(t, \"grandchild\", r.Name)\n\t\t\t}),\n\t\t)\n\n\t\tapp := fxtest.New(t,\n\t\t\tchild,\n\t\t\tfx.WithLogger(func(spy *NamedSpy) fxevent.Logger {\n\t\t\t\tassert.Equal(t, \"app\", spy.name)\n\t\t\t\treturn spy\n\t\t\t}),\n\t\t\tfx.Invoke(func(r *Foo) {\n\t\t\t\tassert.Equal(t, \"grandchild\", r.Name)\n\t\t\t}),\n\t\t)\n\n\t\tassert.Equal(t, []string{\n\t\t\t\"Supplied\", \"Provided\", \"Replaced\", \"BeforeRun\", \"Run\", \"BeforeRun\", \"Run\", \"LoggerInitialized\",\n\t\t\t// Invoke logged twice, once from child and another from grandchild\n\t\t\t\"Invoking\", \"BeforeRun\", \"Run\", \"Invoked\", \"Invoking\", \"Invoked\",\n\t\t}, childSpy.EventTypes(), \"events from grandchild also logged in child logger\")\n\n\t\tassert.Equal(t, []string{\n\t\t\t\"Provided\", \"Provided\", \"Provided\",\n\t\t\t\"LoggerInitialized\", \"Invoking\", \"Invoked\",\n\t\t}, appSpy.EventTypes(), \"events from modules do not appear in app logger\")\n\n\t\tappSpy.Reset()\n\t\tchildSpy.Reset()\n\n\t\tapp.RequireStart().RequireStop()\n\n\t\trequire.NoError(t, app.Err())\n\n\t\tassert.Equal(t, []string{\"Started\", \"Stopped\"}, appSpy.EventTypes())\n\t\tassert.Empty(t, childSpy.EventTypes())\n\t})\n}\n\nfunc TestModuleFailures(t *testing.T) {\n\tt.Parallel()\n\n\tt.Run(\"invoke from submodule failed\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\ttype A struct{}\n\t\ttype B struct{}\n\n\t\tsub := fx.Module(\"sub\",\n\t\t\tfx.Provide(func() *A { return &A{} }),\n\t\t\tfx.Invoke(func(*A, *B) { // missing dependency.\n\t\t\t\trequire.Fail(t, \"this should not be called\")\n\t\t\t}),\n\t\t)\n\n\t\tapp := NewForTest(t,\n\t\t\tsub,\n\t\t\tfx.Invoke(func(a *A) {\n\t\t\t\tassert.NotNil(t, a)\n\t\t\t}),\n\t\t)\n\n\t\terr := app.Err()\n\t\trequire.Error(t, err)\n\t\tassert.Contains(t, err.Error(), \"missing type: *fx_test.B\")\n\t})\n\n\tt.Run(\"provide the same dependency from multiple modules\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\ttype A struct{}\n\n\t\tapp := NewForTest(t,\n\t\t\tfx.Module(\"mod1\", fx.Provide(func() A { return A{} })),\n\t\t\tfx.Module(\"mod2\", fx.Provide(func() A { return A{} })),\n\t\t\tfx.Invoke(func(a A) {}),\n\t\t)\n\n\t\terr := app.Err()\n\t\trequire.Error(t, err)\n\t\tassert.Contains(t, err.Error(), \"already provided by \")\n\t})\n\n\tt.Run(\"providing Modules should fail\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tapp := NewForTest(t,\n\t\t\tfx.Module(\"module\",\n\t\t\t\tfx.Provide(\n\t\t\t\t\tfx.Module(\"module\"),\n\t\t\t\t),\n\t\t\t),\n\t\t)\n\t\terr := app.Err()\n\t\trequire.Error(t, err)\n\t\tassert.Contains(t, err.Error(), \"fx.Option should be passed to fx.New directly, not to fx.Provide\")\n\t})\n\n\tt.Run(\"invoking Modules should fail\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tapp := NewForTest(t,\n\t\t\tfx.Module(\"module\",\n\t\t\t\tfx.Invoke(\n\t\t\t\t\tfx.Invoke(\"module\"),\n\t\t\t\t),\n\t\t\t),\n\t\t)\n\t\terr := app.Err()\n\t\trequire.Error(t, err)\n\t\tassert.Contains(t, err.Error(), \"fx.Option should be passed to fx.New directly, not to fx.Invoke\")\n\t})\n\n\tt.Run(\"annotate failure in Module\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\ttype A struct{}\n\t\tnewA := func() A {\n\t\t\treturn A{}\n\t\t}\n\n\t\tapp := NewForTest(t,\n\t\t\tfx.Module(\"module\",\n\t\t\t\tfx.Provide(fx.Annotate(newA,\n\t\t\t\t\tfx.ParamTags(`name:\"A1\"`),\n\t\t\t\t\tfx.ParamTags(`name:\"A2\"`),\n\t\t\t\t)),\n\t\t\t),\n\t\t)\n\t\terr := app.Err()\n\t\trequire.Error(t, err)\n\n\t\tassert.Contains(t, err.Error(), \"encountered error while applying annotation\")\n\t\tassert.Contains(t, err.Error(), \"cannot apply more than one line of ParamTags\")\n\t})\n\n\tt.Run(\"soft provided to fx.Out struct\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\ttype Result struct {\n\t\t\tfx.Out\n\n\t\t\tBars []int `group:\"bar,soft\"`\n\t\t}\n\t\tapp := NewForTest(t,\n\t\t\tfx.Provide(func() Result { return Result{Bars: []int{1, 2, 3}} }),\n\t\t)\n\t\terr := app.Err()\n\t\trequire.Error(t, err, \"failed to create app\")\n\t\tassert.Contains(t, err.Error(), \"cannot use soft with result value groups\")\n\t})\n\n\tt.Run(\"provider in Module fails\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\ttype A struct{}\n\t\ttype B struct{}\n\n\t\tnewA := func() (A, error) {\n\t\t\treturn A{}, nil\n\t\t}\n\t\tnewB := func() (B, error) {\n\t\t\treturn B{}, errors.New(\"minor sadness\")\n\t\t}\n\n\t\tapp := NewForTest(t,\n\t\t\tfx.Module(\"module\",\n\t\t\t\tfx.Provide(\n\t\t\t\t\tnewA,\n\t\t\t\t\tnewB,\n\t\t\t\t),\n\t\t\t),\n\t\t\tfx.Invoke(func(A, B) {\n\t\t\t\tassert.Fail(t, \"this should never run\")\n\t\t\t}),\n\t\t)\n\n\t\terr := app.Err()\n\t\trequire.Error(t, err)\n\t\tassert.Contains(t, err.Error(), \"failed to build fx_test.B\")\n\t\tassert.Contains(t, err.Error(), \"minor sadness\")\n\t})\n\n\tt.Run(\"invalid Options in Module\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\ttests := []struct {\n\t\t\tdesc string\n\t\t\topt  fx.Option\n\t\t}{\n\t\t\t{\n\t\t\t\tdesc: \"StartTimeout Option\",\n\t\t\t\topt:  fx.StartTimeout(time.Second),\n\t\t\t},\n\t\t\t{\n\t\t\t\tdesc: \"StopTimeout Option\",\n\t\t\t\topt:  fx.StopTimeout(time.Second),\n\t\t\t},\n\t\t\t{\n\t\t\t\tdesc: \"Logger Option\",\n\t\t\t\topt:  fx.Logger(log.New(&bytes.Buffer{}, \"\", 0)),\n\t\t\t},\n\t\t}\n\n\t\tfor _, tt := range tests {\n\t\t\tt.Run(tt.desc, func(t *testing.T) {\n\t\t\t\tt.Parallel()\n\n\t\t\t\tapp := NewForTest(t,\n\t\t\t\t\tfx.Module(\"module\",\n\t\t\t\t\t\ttt.opt,\n\t\t\t\t\t),\n\t\t\t\t)\n\t\t\t\trequire.Error(t, app.Err())\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"invalid WithLogger in Module\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tvar spy fxlog.Spy\n\n\t\tspyAsLogger := fx.Options(\n\t\t\tfx.Supply(&spy),\n\t\t\tfx.WithLogger(func(spy *fxlog.Spy) fxevent.Logger {\n\t\t\t\treturn spy\n\t\t\t}),\n\t\t)\n\n\t\tdefaultModuleOpts := fx.Options(\n\t\t\tfx.Provide(func() string {\n\t\t\t\treturn \"redis\"\n\t\t\t}),\n\t\t\tfx.Invoke(func(s string) {\n\t\t\t\tassert.Fail(t, \"this should never run\")\n\t\t\t}),\n\t\t)\n\n\t\ttests := []struct {\n\t\t\tdesc            string\n\t\t\tgiveModuleOpts  fx.Option\n\t\t\tgiveAppOpts     fx.Option\n\t\t\twantErrContains []string\n\t\t\twantEvents      []string\n\t\t}{\n\t\t\t{\n\t\t\t\tdesc:           \"error in Provide shows logs in module\",\n\t\t\t\tgiveModuleOpts: fx.Options(spyAsLogger, fx.Provide(&bytes.Buffer{})), // not passing in a constructor\n\t\t\t\tgiveAppOpts:    fx.Options(),\n\t\t\t\twantErrContains: []string{\n\t\t\t\t\t\"must provide constructor function, got  (type *bytes.Buffer)\",\n\t\t\t\t},\n\t\t\t\twantEvents: []string{\n\t\t\t\t\t\"Supplied\", \"Provided\", \"BeforeRun\", \"Run\", \"LoggerInitialized\",\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tdesc: \"logger in module failed to build\",\n\t\t\t\tgiveModuleOpts: fx.WithLogger(func() (fxevent.Logger, error) {\n\t\t\t\t\treturn nil, errors.New(\"error building logger\")\n\t\t\t\t}),\n\t\t\t\tgiveAppOpts:     spyAsLogger,\n\t\t\t\twantErrContains: []string{\"error building logger\"},\n\t\t\t\twantEvents: []string{\n\t\t\t\t\t\"Provided\", \"Provided\", \"Provided\", \"Supplied\", \"BeforeRun\", \"Run\",\n\t\t\t\t\t\"LoggerInitialized\", \"Provided\", \"LoggerInitialized\",\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tdesc: \"logger dependency in module failed to build\",\n\t\t\t\tgiveModuleOpts: fx.Options(\n\t\t\t\t\tfx.Provide(func() (*zap.Logger, error) {\n\t\t\t\t\t\treturn nil, errors.New(\"error building logger dependency\")\n\t\t\t\t\t}),\n\t\t\t\t\tfx.WithLogger(func(log *zap.Logger) fxevent.Logger {\n\t\t\t\t\t\tt.Errorf(\"WithLogger must not be called\")\n\t\t\t\t\t\tpanic(\"must not be called\")\n\t\t\t\t\t}),\n\t\t\t\t),\n\t\t\t\tgiveAppOpts:     spyAsLogger,\n\t\t\t\twantErrContains: []string{\"error building logger dependency\"},\n\t\t\t\twantEvents: []string{\n\t\t\t\t\t\"Provided\", \"Provided\", \"Provided\", \"Supplied\", \"BeforeRun\", \"Run\",\n\t\t\t\t\t\"LoggerInitialized\", \"Provided\", \"Provided\", \"BeforeRun\", \"Run\", \"LoggerInitialized\",\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tdesc:           \"Invalid input for WithLogger\",\n\t\t\t\tgiveModuleOpts: fx.WithLogger(&fxlog.Spy{}), // not passing in a constructor for WithLogger\n\t\t\t\tgiveAppOpts:    spyAsLogger,\n\t\t\t\twantErrContains: []string{\n\t\t\t\t\t\"fx.WithLogger\", \"from:\", \"Failed\",\n\t\t\t\t},\n\t\t\t\twantEvents: []string{\n\t\t\t\t\t\"Provided\", \"Provided\", \"Provided\", \"Supplied\", \"BeforeRun\", \"Run\",\n\t\t\t\t\t\"LoggerInitialized\", \"Provided\", \"LoggerInitialized\",\n\t\t\t\t},\n\t\t\t},\n\t\t}\n\t\tfor _, tt := range tests {\n\t\t\tspy.Reset()\n\t\t\tt.Run(tt.desc, func(t *testing.T) {\n\t\t\t\tredis := fx.Module(\"redis\",\n\t\t\t\t\ttt.giveModuleOpts,\n\t\t\t\t\tdefaultModuleOpts,\n\t\t\t\t)\n\n\t\t\t\tapp := fx.New(\n\t\t\t\t\ttt.giveAppOpts,\n\t\t\t\t\tredis,\n\t\t\t\t\tfx.Invoke(func(s string) {\n\t\t\t\t\t\tassert.Fail(t, \"this should never run\")\n\t\t\t\t\t}),\n\t\t\t\t)\n\t\t\t\terr := app.Err()\n\t\t\t\trequire.Error(t, err)\n\t\t\t\tfor _, contains := range tt.wantErrContains {\n\t\t\t\t\tassert.Contains(t, err.Error(), contains)\n\t\t\t\t}\n\n\t\t\t\tassert.Equal(t,\n\t\t\t\t\ttt.wantEvents,\n\t\t\t\t\tspy.EventTypes(),\n\t\t\t\t)\n\t\t\t})\n\t\t}\n\t})\n}\n"
  },
  {
    "path": "populate.go",
    "content": "// Copyright (c) 2019 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage fx\n\nimport (\n\t\"fmt\"\n\t\"reflect\"\n)\n\n// Populate sets targets with values from the dependency injection container\n// during application initialization. All targets must be pointers to the\n// values that must be populated. Pointers to structs that embed In are\n// supported, which can be used to populate multiple values in a struct.\n//\n// Annotating each pointer with ParamTags is also supported as a shorthand\n// to passing a pointer to a struct that embeds In with field tags. For example:\n//\n//\t var a A\n//\t var b B\n//\t fx.Populate(\n//\t\tfx.Annotate(\n//\t\t\t\t&a,\n//\t\t\t\tfx.ParamTags(`name:\"A\"`)\n//\t \t),\n//\t\tfx.Annotate(\n//\t\t\t\t&b,\n//\t\t\t\tfx.ParamTags(`name:\"B\"`)\n//\t \t)\n//\t )\n//\n// Code above is equivalent to the following:\n//\n//\ttype Target struct {\n//\t\tfx.In\n//\n//\t\ta A `name:\"A\"`\n//\t\tb B `name:\"B\"`\n//\t}\n//\tvar target Target\n//\t...\n//\tfx.Populate(&target)\n//\n// This is most helpful in unit tests: it lets tests leverage Fx's automatic\n// constructor wiring to build a few structs, but then extract those structs\n// for further testing.\nfunc Populate(targets ...any) Option {\n\t// Validate all targets are non-nil pointers.\n\tfields := make([]reflect.StructField, len(targets)+1)\n\tfields[0] = reflect.StructField{\n\t\tName:      \"In\",\n\t\tType:      reflect.TypeOf(In{}),\n\t\tAnonymous: true,\n\t}\n\tfor i, t := range targets {\n\t\tif t == nil {\n\t\t\treturn Error(fmt.Errorf(\"failed to Populate: target %v is nil\", i+1))\n\t\t}\n\t\tvar (\n\t\t\trt  reflect.Type\n\t\t\ttag reflect.StructTag\n\t\t)\n\t\tswitch t := t.(type) {\n\t\tcase annotated:\n\t\t\trt = reflect.TypeOf(t.Target)\n\t\t\ttag = reflect.StructTag(t.ParamTags[0])\n\t\t\ttargets[i] = t.Target\n\t\tdefault:\n\t\t\trt = reflect.TypeOf(t)\n\t\t}\n\t\tif rt.Kind() != reflect.Ptr {\n\t\t\treturn Error(fmt.Errorf(\"failed to Populate: target %v is not a pointer type, got %T\", i+1, t))\n\t\t}\n\t\tfields[i+1] = reflect.StructField{\n\t\t\tName: fmt.Sprintf(\"Field%d\", i),\n\t\t\tType: rt.Elem(),\n\t\t\tTag:  tag,\n\t\t}\n\t}\n\n\t// Build a function that looks like:\n\t//\n\t// func(t1 T1, t2 T2, ...) {\n\t//   *targets[0] = t1\n\t//   *targets[1] = t2\n\t//   [...]\n\t// }\n\t//\n\tfnType := reflect.FuncOf([]reflect.Type{reflect.StructOf(fields)}, nil, false /* variadic */)\n\tfn := reflect.MakeFunc(fnType, func(args []reflect.Value) []reflect.Value {\n\t\targ := args[0]\n\t\tfor i, target := range targets {\n\t\t\treflect.ValueOf(target).Elem().Set(arg.Field(i + 1))\n\t\t}\n\t\treturn nil\n\t})\n\treturn Invoke(fn.Interface())\n}\n"
  },
  {
    "path": "populate_example_test.go",
    "content": "// Copyright (c) 2019 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage fx_test\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"go.uber.org/fx\"\n)\n\nfunc ExamplePopulate() {\n\t// Some external module that provides a user name.\n\ttype Username string\n\tUserModule := fx.Provide(func() Username { return \"john\" })\n\n\t// We want to use Fx to wire up our constructors, but don't actually want to\n\t// run the application - we just want to yank out the user name.\n\t//\n\t// This is common in unit tests, and is even easier with the fxtest\n\t// package's RequireStart and RequireStop helpers.\n\tvar user Username\n\tapp := fx.New(\n\t\tUserModule,\n\t\tfx.NopLogger, // silence test output\n\t\tfx.Populate(&user),\n\t)\n\tif err := app.Start(context.Background()); err != nil {\n\t\tpanic(err)\n\t}\n\tdefer app.Stop(context.Background())\n\n\tfmt.Println(user)\n\n\t// Output:\n\t// john\n}\n"
  },
  {
    "path": "populate_test.go",
    "content": "// Copyright (c) 2019 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage fx_test\n\nimport (\n\t\"io\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\t. \"go.uber.org/fx\"\n\t\"go.uber.org/fx/fxtest\"\n)\n\nfunc TestPopulate(t *testing.T) {\n\tt.Parallel()\n\n\t// We make sure t1 has a size so when we compare pointers, 2 different\n\t// objects are not equal.\n\ttype t1 struct {\n\t\tbuf [1024]byte\n\t}\n\ttype t2 struct{}\n\n\t_ = new(t1).buf // buf is unused\n\n\tt.Run(\"populate nothing\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tapp := fxtest.New(t,\n\t\t\tProvide(func() *t1 { panic(\"should not be called \") }),\n\t\t\tPopulate(),\n\t\t)\n\t\tapp.RequireStart().RequireStop()\n\t})\n\n\tt.Run(\"populate single\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tvar v1 *t1\n\t\tapp := fxtest.New(t,\n\t\t\tProvide(func() *t1 { return &t1{} }),\n\t\t\tPopulate(&v1),\n\t\t)\n\t\tapp.RequireStart().RequireStop()\n\t\trequire.NotNil(t, v1, \"did not populate value\")\n\t})\n\n\tt.Run(\"populate interface\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tvar reader io.Reader\n\t\tapp := fxtest.New(t,\n\t\t\tProvide(func() io.Reader { return strings.NewReader(\"hello world\") }),\n\t\t\tPopulate(&reader),\n\t\t)\n\t\tapp.RequireStart().RequireStop()\n\n\t\tbs, err := io.ReadAll(reader)\n\t\trequire.NoError(t, err, \"Failed to use populated io.Reader\")\n\t\tassert.Equal(t, \"hello world\", string(bs), \"Unexpected reader\")\n\t})\n\n\tt.Run(\"populate multiple inline values\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tvar (\n\t\t\tv1 *t1\n\t\t\tv2 *t2\n\t\t)\n\t\tapp := fxtest.New(t,\n\t\t\tProvide(func() *t1 { return &t1{} }),\n\t\t\tProvide(func() *t2 { return &t2{} }),\n\n\t\t\tPopulate(&v1),\n\t\t\tPopulate(&v2),\n\t\t)\n\t\tapp.RequireStart().RequireStop()\n\n\t\trequire.NotNil(t, v1, \"did not populate argument 1\")\n\t\trequire.NotNil(t, v2, \"did not populate argument 2\")\n\t})\n\n\tt.Run(\"populate fx.In struct\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\ttargets := struct {\n\t\t\tIn\n\n\t\t\tV1 *t1\n\t\t\tV2 *t2\n\t\t}{}\n\n\t\tapp := fxtest.New(t,\n\t\t\tProvide(func() *t1 { return &t1{} }),\n\t\t\tProvide(func() *t2 { return &t2{} }),\n\n\t\t\tPopulate(&targets),\n\t\t)\n\t\tapp.RequireStart().RequireStop()\n\n\t\trequire.NotNil(t, targets.V1, \"did not populate field 1\")\n\t\trequire.NotNil(t, targets.V2, \"did not populate field 2\")\n\t})\n\n\tt.Run(\"populate named field\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\ttype result struct {\n\t\t\tOut\n\n\t\t\tV1 *t1 `name:\"n1\"`\n\t\t\tV2 *t1 `name:\"n2\"`\n\t\t}\n\n\t\ttargets := struct {\n\t\t\tIn\n\n\t\t\tV1 *t1 `name:\"n1\"`\n\t\t\tV2 *t1 `name:\"n2\"`\n\t\t}{}\n\n\t\tapp := fxtest.New(t,\n\t\t\tProvide(func() result {\n\t\t\t\treturn result{\n\t\t\t\t\tV1: &t1{},\n\t\t\t\t\tV2: &t1{},\n\t\t\t\t}\n\t\t\t}),\n\n\t\t\tPopulate(&targets),\n\t\t)\n\t\tapp.RequireStart().RequireStop()\n\n\t\trequire.NotNil(t, targets.V1, \"did not populate field 1\")\n\t\trequire.NotNil(t, targets.V2, \"did not populate field 2\")\n\t\t// Cannot use assert.Equal here as we want to compare pointers.\n\t\tassert.False(t, targets.V1 == targets.V2, \"fields should be different\")\n\t})\n\n\tt.Run(\"annotated populate\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\ttype result struct {\n\t\t\tOut\n\n\t\t\tV1 *t1 `name:\"n1\"`\n\t\t\tV2 *t1 `name:\"n2\"`\n\t\t}\n\t\tvar v1, v2 *t1\n\t\tapp := fxtest.New(t,\n\t\t\tProvide(func() result {\n\t\t\t\treturn result{\n\t\t\t\t\tV1: &t1{},\n\t\t\t\t\tV2: &t1{},\n\t\t\t\t}\n\t\t\t}),\n\t\t\tPopulate(\n\t\t\t\tAnnotate(\n\t\t\t\t\t&v1,\n\t\t\t\t\tParamTags(`name:\"n1\"`),\n\t\t\t\t),\n\t\t\t\tAnnotate(\n\t\t\t\t\t&v2,\n\t\t\t\t\tParamTags(`name:\"n2\"`),\n\t\t\t\t),\n\t\t\t),\n\t\t)\n\t\tapp.RequireStart().RequireStop()\n\n\t\trequire.NotNil(t, v1, \"did not populate argument 1\")\n\t\trequire.NotNil(t, v2, \"did not populate argument 2\")\n\t\t// Cannot use assert.Equal here as we want to compare pointers.\n\t\tassert.False(t, v1 == v2, \"values should be different\")\n\t})\n\n\tt.Run(\"annotated populate with annotated provide\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tvar v1, v2 *t1\n\t\tapp := fxtest.New(t,\n\t\t\tProvide(\n\t\t\t\tAnnotate(\n\t\t\t\t\tfunc() (*t1, *t1) {\n\t\t\t\t\t\treturn &t1{}, &t1{}\n\t\t\t\t\t},\n\t\t\t\t\tResultTags(`name:\"n1\"`, `name:\"n2\"`),\n\t\t\t\t),\n\t\t\t),\n\t\t\tPopulate(\n\t\t\t\tAnnotate(\n\t\t\t\t\t&v1,\n\t\t\t\t\tParamTags(`name:\"n1\"`),\n\t\t\t\t),\n\t\t\t\tAnnotate(\n\t\t\t\t\t&v2,\n\t\t\t\t\tParamTags(`name:\"n2\"`),\n\t\t\t\t),\n\t\t\t),\n\t\t)\n\t\tapp.RequireStart().RequireStop()\n\n\t\trequire.NotNil(t, v1, \"did not populate argument 1\")\n\t\trequire.NotNil(t, v2, \"did not populate argument 2\")\n\t\t// Cannot use assert.Equal here as we want to compare pointers.\n\t\tassert.False(t, v1 == v2, \"values should be different\")\n\t})\n\n\tt.Run(\"populate group\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\ttype result struct {\n\t\t\tOut\n\n\t\t\tV1 *t1 `group:\"g\"`\n\t\t\tV2 *t1 `group:\"g\"`\n\t\t}\n\n\t\ttargets := struct {\n\t\t\tIn\n\n\t\t\tGroup []*t1 `group:\"g\"`\n\t\t}{}\n\n\t\tapp := fxtest.New(t,\n\t\t\tProvide(func() result {\n\t\t\t\treturn result{\n\t\t\t\t\tV1: &t1{},\n\t\t\t\t\tV2: &t1{},\n\t\t\t\t}\n\t\t\t}),\n\n\t\t\tPopulate(&targets),\n\t\t)\n\t\tapp.RequireStart().RequireStop()\n\n\t\trequire.Len(t, targets.Group, 2, \"Expected group to have 2 values\")\n\t\trequire.NotNil(t, targets.Group[0], \"did not populate group value 1\")\n\t\trequire.NotNil(t, targets.Group[1], \"did not populate group value 2\")\n\t\t// Cannot use assert.Equal here as we want to compare pointers.\n\t\tassert.False(t, targets.Group[0] == targets.Group[1], \"group values should be different\")\n\t})\n}\n\nfunc TestPopulateErrors(t *testing.T) {\n\tt.Parallel()\n\n\ttype t1 struct{}\n\ttype container struct {\n\t\tIn\n\n\t\tT1 t1\n\t}\n\ttype containerNoIn struct {\n\t\tT1 t1\n\t}\n\tfn := func() {}\n\tvar v *t1\n\n\ttests := []struct {\n\t\tmsg     string\n\t\topt     Option\n\t\twantErr string\n\t}{\n\t\t{\n\t\t\tmsg:     \"inline value\",\n\t\t\topt:     Populate(t1{}),\n\t\t\twantErr: \"not a pointer\",\n\t\t},\n\t\t{\n\t\t\tmsg:     \"container value\",\n\t\t\topt:     Populate(container{}),\n\t\t\twantErr: \"not a pointer\",\n\t\t},\n\t\t{\n\t\t\tmsg:     \"container pointer without fx.In\",\n\t\t\topt:     Populate(&containerNoIn{}),\n\t\t\twantErr: \"missing type: fx_test.containerNoIn\",\n\t\t},\n\t\t{\n\t\t\tmsg:     \"function\",\n\t\t\topt:     Populate(fn),\n\t\t\twantErr: \"not a pointer\",\n\t\t},\n\t\t{\n\t\t\tmsg:     \"function pointer\",\n\t\t\topt:     Populate(&fn),\n\t\t\twantErr: \"missing type: func()\",\n\t\t},\n\t\t{\n\t\t\tmsg:     \"invalid last argument\",\n\t\t\topt:     Populate(&v, t1{}),\n\t\t\twantErr: \"target 2 is not a pointer type\",\n\t\t},\n\t\t{\n\t\t\tmsg:     \"nil argument\",\n\t\t\topt:     Populate(&v, nil, &v),\n\t\t\twantErr: \"target 2 is nil\",\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tapp := NewForTest(t,\n\t\t\tNopLogger,\n\t\t\tProvide(func() *t1 { return &t1{} }),\n\n\t\t\ttt.opt,\n\t\t)\n\t\trequire.Error(t, app.Err())\n\t\tassert.Contains(t, app.Err().Error(), tt.wantErr)\n\t}\n}\n\nfunc TestPopulateValidateApp(t *testing.T) {\n\tt.Parallel()\n\n\ttype t1 struct{}\n\ttype container struct {\n\t\tIn\n\n\t\tT1 t1\n\t}\n\ttype containerNoIn struct {\n\t\tT1 t1\n\t}\n\tfn := func() {}\n\tvar v *t1\n\n\ttests := []struct {\n\t\tmsg     string\n\t\topts    []any\n\t\twantErr string\n\t}{\n\t\t{\n\t\t\tmsg:     \"inline value\",\n\t\t\topts:    []any{t1{}},\n\t\t\twantErr: \"not a pointer\",\n\t\t},\n\t\t{\n\t\t\tmsg:     \"container value\",\n\t\t\topts:    []any{container{}},\n\t\t\twantErr: \"not a pointer\",\n\t\t},\n\t\t{\n\t\t\tmsg:     \"container pointer without fx.In\",\n\t\t\topts:    []any{&containerNoIn{}},\n\t\t\twantErr: \"missing type: fx_test.containerNoIn\",\n\t\t},\n\t\t{\n\t\t\tmsg:     \"function\",\n\t\t\topts:    []any{fn},\n\t\t\twantErr: \"not a pointer\",\n\t\t},\n\t\t{\n\t\t\tmsg:     \"function pointer\",\n\t\t\topts:    []any{&fn},\n\t\t\twantErr: \"missing type: func()\",\n\t\t},\n\t\t{\n\t\t\tmsg:     \"invalid last argument\",\n\t\t\topts:    []any{&v, t1{}},\n\t\t\twantErr: \"target 2 is not a pointer type\",\n\t\t},\n\t\t{\n\t\t\tmsg:     \"nil argument\",\n\t\t\topts:    []any{&v, nil, &v},\n\t\t\twantErr: \"target 2 is nil\",\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.msg, func(t *testing.T) {\n\t\t\tt.Parallel()\n\n\t\t\ttestOpts := []Option{\n\t\t\t\tNopLogger,\n\t\t\t\tProvide(func() *t1 { return &t1{} }),\n\t\t\t\tPopulate(tt.opts...),\n\t\t\t}\n\n\t\t\terr := validateTestApp(t, testOpts...)\n\t\t\trequire.Error(t, err)\n\t\t\tassert.Contains(t, err.Error(), tt.wantErr)\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "printer_writer.go",
    "content": "// Copyright (c) 2020-2021 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage fx\n\nimport \"io\"\n\ntype printerWriter struct{ p Printer }\n\n// writerFromPrinter returns an implementation of io.Writer used to support\n// Logger option which implements Printer interface.\nfunc writerFromPrinter(p Printer) io.Writer {\n\treturn &printerWriter{p: p}\n}\n\nfunc (w *printerWriter) Write(b []byte) (n int, err error) {\n\tw.p.Printf(string(b))\n\treturn len(b), nil\n}\n"
  },
  {
    "path": "provide.go",
    "content": "// Copyright (c) 2022 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage fx\n\nimport (\n\t\"fmt\"\n\t\"reflect\"\n\t\"strings\"\n\n\t\"go.uber.org/dig\"\n\t\"go.uber.org/fx/internal/fxreflect\"\n)\n\n// Provide registers any number of constructor functions, teaching the\n// application how to instantiate various types. The supplied constructor\n// function(s) may depend on other types available in the application, must\n// return one or more objects, and may return an error. For example:\n//\n//\t// Constructs type *C, depends on *A and *B.\n//\tfunc(*A, *B) *C\n//\n//\t// Constructs type *C, depends on *A and *B, and indicates failure by\n//\t// returning an error.\n//\tfunc(*A, *B) (*C, error)\n//\n//\t// Constructs types *B and *C, depends on *A, and can fail.\n//\tfunc(*A) (*B, *C, error)\n//\n// The order in which constructors are provided doesn't matter, and passing\n// multiple Provide options appends to the application's collection of\n// constructors. Constructors are called only if one or more of their returned\n// types are needed, and their results are cached for reuse (so instances of a\n// type are effectively singletons within an application). Taken together,\n// these properties make it perfectly reasonable to Provide a large number of\n// constructors even if only a fraction of them are used.\n//\n// See the documentation of the In and Out types for advanced features,\n// including optional parameters and named instances.\n//\n// See the documentation for [Private] for restricting access to constructors.\n//\n// Constructor functions should perform as little external interaction as\n// possible, and should avoid spawning goroutines. Things like server listen\n// loops, background timer loops, and background processing goroutines should\n// instead be managed using Lifecycle callbacks.\nfunc Provide(constructors ...any) Option {\n\treturn provideOption{\n\t\tTargets: constructors,\n\t\tStack:   fxreflect.CallerStack(1, 0),\n\t}\n}\n\ntype provideOption struct {\n\tTargets []any\n\tStack   fxreflect.Stack\n}\n\nfunc (o provideOption) apply(mod *module) {\n\tvar private bool\n\n\ttargets := make([]any, 0, len(o.Targets))\n\tfor _, target := range o.Targets {\n\t\tif _, ok := target.(privateOption); ok {\n\t\t\tprivate = true\n\t\t\tcontinue\n\t\t}\n\t\ttargets = append(targets, target)\n\t}\n\n\tfor _, target := range targets {\n\t\tmod.provides = append(mod.provides, provide{\n\t\t\tTarget:  target,\n\t\t\tStack:   o.Stack,\n\t\t\tPrivate: private,\n\t\t})\n\t}\n}\n\ntype privateOption struct{}\n\n// Private is an option that can be passed as an argument to [Provide] or [Supply] to\n// restrict access to the constructors being provided. Specifically,\n// corresponding constructors can only be used within the current module\n// or modules the current module contains. Other modules that contain this\n// module won't be able to use the constructor.\n//\n// For example, the following would fail because the app doesn't have access\n// to the inner module's constructor.\n//\n//\tfx.New(\n//\t\tfx.Module(\"SubModule\", fx.Provide(func() int { return 0 }, fx.Private)),\n//\t\tfx.Invoke(func(a int) {}),\n//\t)\nvar Private = privateOption{}\n\nfunc (o provideOption) String() string {\n\titems := make([]string, len(o.Targets))\n\tfor i, c := range o.Targets {\n\t\titems[i] = fxreflect.FuncName(c)\n\t}\n\treturn fmt.Sprintf(\"fx.Provide(%s)\", strings.Join(items, \", \"))\n}\n\nfunc runProvide(c container, p provide, opts ...dig.ProvideOption) error {\n\tconstructor := p.Target\n\tif _, ok := constructor.(Option); ok {\n\t\treturn fmt.Errorf(\"fx.Option should be passed to fx.New directly, \"+\n\t\t\t\"not to fx.Provide: fx.Provide received %v from:\\n%+v\",\n\t\t\tconstructor, p.Stack)\n\t}\n\n\tswitch constructor := constructor.(type) {\n\tcase annotationError:\n\t\t// fx.Annotate failed. Turn it into an Fx error.\n\t\treturn fmt.Errorf(\n\t\t\t\"encountered error while applying annotation using fx.Annotate to %s: %w\",\n\t\t\tfxreflect.FuncName(constructor.target), constructor.err)\n\n\tcase annotated:\n\t\tctor, err := constructor.Build()\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"fx.Provide(%v) from:\\n%+vFailed: %w\", constructor, p.Stack, err)\n\t\t}\n\n\t\topts = append(opts, dig.LocationForPC(constructor.FuncPtr))\n\t\tif err := c.Provide(ctor, opts...); err != nil {\n\t\t\treturn fmt.Errorf(\"fx.Provide(%v) from:\\n%+vFailed: %w\", constructor, p.Stack, err)\n\t\t}\n\n\tcase Annotated:\n\t\tann := constructor\n\t\tswitch {\n\t\tcase len(ann.Group) > 0 && len(ann.Name) > 0:\n\t\t\treturn fmt.Errorf(\n\t\t\t\t\"fx.Annotated may specify only one of Name or Group: received %v from:\\n%+v\",\n\t\t\t\tann, p.Stack)\n\t\tcase len(ann.Name) > 0:\n\t\t\topts = append(opts, dig.Name(ann.Name))\n\t\tcase len(ann.Group) > 0:\n\t\t\topts = append(opts, dig.Group(ann.Group))\n\t\t}\n\n\t\tif err := c.Provide(ann.Target, opts...); err != nil {\n\t\t\treturn fmt.Errorf(\"fx.Provide(%v) from:\\n%+vFailed: %w\", ann, p.Stack, err)\n\t\t}\n\n\tdefault:\n\t\tif reflect.TypeOf(constructor).Kind() == reflect.Func {\n\t\t\tft := reflect.ValueOf(constructor).Type()\n\n\t\t\tfor i := 0; i < ft.NumOut(); i++ {\n\t\t\t\tt := ft.Out(i)\n\n\t\t\t\tif t == reflect.TypeOf(Annotated{}) {\n\t\t\t\t\treturn fmt.Errorf(\n\t\t\t\t\t\t\"fx.Annotated should be passed to fx.Provide directly, \"+\n\t\t\t\t\t\t\t\"it should not be returned by the constructor: \"+\n\t\t\t\t\t\t\t\"fx.Provide received %v from:\\n%+v\",\n\t\t\t\t\t\tfxreflect.FuncName(constructor), p.Stack)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif err := c.Provide(constructor, opts...); err != nil {\n\t\t\treturn fmt.Errorf(\"fx.Provide(%v) from:\\n%+vFailed: %w\", fxreflect.FuncName(constructor), p.Stack, err)\n\t\t}\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "replace.go",
    "content": "// Copyright (c) 2022 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage fx\n\nimport (\n\t\"fmt\"\n\t\"reflect\"\n\t\"strings\"\n\n\t\"go.uber.org/fx/internal/fxreflect\"\n)\n\n// Replace provides instantiated values for graph modification as if\n// they had been provided using a decorator with fx.Decorate.\n// The most specific type of each value (as determined by reflection) is used.\n//\n// Refer to the documentation on fx.Decorate to see how graph modifications\n// work with fx.Module.\n//\n// This serves a purpose similar to what fx.Supply does for fx.Provide.\n//\n// For example, given,\n//\n//\tvar log *zap.Logger = ...\n//\n// The following two forms are equivalent.\n//\n//\tfx.Replace(log)\n//\n//\tfx.Decorate(\n//\t\tfunc() *zap.Logger {\n//\t\t\treturn log\n//\t\t},\n//\t)\n//\n// Replace panics if a value (or annotation target) is an untyped nil or an error.\n//\n// # Replace Caveats\n//\n// As mentioned above, Replace uses the most specific type of the provided\n// value. For interface values, this refers to the type of the implementation,\n// not the interface. So if you try to replace an io.Writer, fx.Replace will\n// use the type of the implementation.\n//\n//\tvar stderr io.Writer = os.Stderr\n//\tfx.Replace(stderr)\n//\n// Is equivalent to,\n//\n//\tfx.Decorate(func() *os.File { return os.Stderr })\n//\n// This is typically NOT what you intended. To replace the io.Writer in the\n// container with the value above, we need to use the fx.Annotate function with\n// the fx.As annotation.\n//\n//\tfx.Replace(\n//\t\tfx.Annotate(os.Stderr, fx.As(new(io.Writer)))\n//\t)\nfunc Replace(values ...any) Option {\n\tdecorators := make([]any, len(values)) // one function per value\n\ttypes := make([]reflect.Type, len(values))\n\tfor i, value := range values {\n\t\tswitch value := value.(type) {\n\t\tcase annotated:\n\t\t\tvar typ reflect.Type\n\t\t\tvalue.Target, typ = newReplaceDecorator(value.Target)\n\t\t\tdecorators[i] = value\n\t\t\ttypes[i] = typ\n\t\tdefault:\n\t\t\tdecorators[i], types[i] = newReplaceDecorator(value)\n\t\t}\n\t}\n\n\treturn replaceOption{\n\t\tTargets: decorators,\n\t\tTypes:   types,\n\t\tStack:   fxreflect.CallerStack(1, 0),\n\t}\n}\n\ntype replaceOption struct {\n\tTargets []any\n\tTypes   []reflect.Type // type of value produced by constructor[i]\n\tStack   fxreflect.Stack\n}\n\nfunc (o replaceOption) apply(m *module) {\n\tfor i, target := range o.Targets {\n\t\tm.decorators = append(m.decorators, decorator{\n\t\t\tTarget:      target,\n\t\t\tStack:       o.Stack,\n\t\t\tIsReplace:   true,\n\t\t\tReplaceType: o.Types[i],\n\t\t})\n\t}\n}\n\nfunc (o replaceOption) String() string {\n\titems := make([]string, 0, len(o.Targets))\n\tfor _, typ := range o.Types {\n\t\titems = append(items, typ.String())\n\t}\n\treturn fmt.Sprintf(\"fx.Replace(%s)\", strings.Join(items, \", \"))\n}\n\n// Returns a function that takes no parameters, and returns the given value.\nfunc newReplaceDecorator(value any) (any, reflect.Type) {\n\tswitch value.(type) {\n\tcase nil:\n\t\tpanic(\"untyped nil passed to fx.Replace\")\n\tcase error:\n\t\tpanic(\"error value passed to fx.Replace\")\n\t}\n\n\ttyp := reflect.TypeOf(value)\n\treturnTypes := []reflect.Type{typ}\n\treturnValues := []reflect.Value{reflect.ValueOf(value)}\n\n\tft := reflect.FuncOf([]reflect.Type{}, returnTypes, false)\n\tfv := reflect.MakeFunc(ft, func([]reflect.Value) []reflect.Value {\n\t\treturn returnValues\n\t})\n\n\treturn fv.Interface(), typ\n}\n"
  },
  {
    "path": "replace_test.go",
    "content": "// Copyright (c) 2022 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage fx_test\n\nimport (\n\t\"errors\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\t\"go.uber.org/fx\"\n\t\"go.uber.org/fx/fxtest\"\n)\n\nfunc TestReplaceSuccess(t *testing.T) {\n\tt.Parallel()\n\n\tt.Run(\"replace a value\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\ttype A struct {\n\t\t\tValue string\n\t\t}\n\t\ta := &A{Value: \"a'\"}\n\t\tapp := fxtest.New(t,\n\t\t\tfx.Provide(func() *A {\n\t\t\t\treturn &A{\n\t\t\t\t\tValue: \"a\",\n\t\t\t\t}\n\t\t\t}),\n\t\t\tfx.Replace(a),\n\t\t\tfx.Invoke(func(a *A) {\n\t\t\t\tassert.Equal(t, \"a'\", a.Value)\n\t\t\t}),\n\t\t)\n\t\tdefer app.RequireStart().RequireStop()\n\t})\n\n\tt.Run(\"replace in a module\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\ttype A struct {\n\t\t\tValue string\n\t\t}\n\n\t\ta := &A{Value: \"A\"}\n\n\t\tapp := fxtest.New(t,\n\t\t\tfx.Module(\"child\",\n\t\t\t\tfx.Replace(a),\n\t\t\t\tfx.Invoke(func(a *A) {\n\t\t\t\t\tassert.Equal(t, \"A\", a.Value)\n\t\t\t\t}),\n\t\t\t),\n\t\t\tfx.Provide(func() *A {\n\t\t\t\treturn &A{\n\t\t\t\t\tValue: \"a\",\n\t\t\t\t}\n\t\t\t}),\n\t\t)\n\t\tdefer app.RequireStart().RequireStop()\n\t})\n\n\tt.Run(\"replace with annotate\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\ttype A struct {\n\t\t\tValue string\n\t\t}\n\n\t\tapp := fxtest.New(t,\n\t\t\tfx.Supply(\n\t\t\t\tfx.Annotate(A{\"A\"}, fx.ResultTags(`name:\"t\"`)),\n\t\t\t),\n\t\t\tfx.Replace(\n\t\t\t\tfx.Annotate(A{\"B\"}, fx.ResultTags(`name:\"t\"`)),\n\t\t\t),\n\t\t\tfx.Invoke(fx.Annotate(func(a A) {\n\t\t\t\tassert.Equal(t, a.Value, \"B\")\n\t\t\t}, fx.ParamTags(`name:\"t\"`))),\n\t\t)\n\t\tdefer app.RequireStart().RequireStop()\n\t})\n\n\tt.Run(\"replace a value group with annotate\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tapp := fxtest.New(t,\n\t\t\tfx.Supply(\n\t\t\t\tfx.Annotate([]string{\"A\", \"B\", \"C\"}, fx.ResultTags(`group:\"t,flatten\"`)),\n\t\t\t),\n\t\t\tfx.Replace(fx.Annotate([]string{\"a\", \"b\", \"c\"}, fx.ResultTags(`group:\"t\"`))),\n\t\t\tfx.Invoke(fx.Annotate(func(ss ...string) {\n\t\t\t\tassert.ElementsMatch(t, []string{\"a\", \"b\", \"c\"}, ss)\n\t\t\t}, fx.ParamTags(`group:\"t\"`))),\n\t\t)\n\t\tdefer app.RequireStart().RequireStop()\n\t})\n\n\tt.Run(\"replace a value group supplied by a child module from root module\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tfoo := fx.Module(\"foo\",\n\t\t\tfx.Supply(\n\t\t\t\tfx.Annotate([]string{\"a\", \"b\", \"c\"}, fx.ResultTags(`group:\"t,flatten\"`)),\n\t\t\t),\n\t\t)\n\n\t\tfxtest.New(t,\n\t\t\tfx.Module(\"wrapfoo\",\n\t\t\t\tfoo,\n\t\t\t\tfx.Replace(\n\t\t\t\t\tfx.Annotate([]string{\"d\", \"e\", \"f\"}, fx.ResultTags(`group:\"t\"`)),\n\t\t\t\t),\n\t\t\t\tfx.Invoke(fx.Annotate(func(ss []string) {\n\t\t\t\t\tassert.ElementsMatch(t, []string{\"d\", \"e\", \"f\"}, ss)\n\t\t\t\t}, fx.ParamTags(`group:\"t\"`))),\n\t\t\t),\n\t\t)\n\t})\n}\n\nfunc TestReplaceFailure(t *testing.T) {\n\tt.Parallel()\n\n\tt.Run(\"replace same value twice\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\ttype A struct {\n\t\t\tValue string\n\t\t}\n\t\ta := &A{Value: \"A\"}\n\t\tapp := NewForTest(t,\n\t\t\tfx.Provide(func() *A {\n\t\t\t\treturn &A{Value: \"a\"}\n\t\t\t}),\n\t\t\tfx.Module(\"child\",\n\t\t\t\tfx.Replace(a),\n\t\t\t\tfx.Replace(a),\n\t\t\t\tfx.Invoke(func(a *A) {\n\t\t\t\t\tassert.Fail(t, \"this should never run\")\n\t\t\t\t}),\n\t\t\t),\n\t\t)\n\t\terr := app.Err()\n\t\tassert.Error(t, err)\n\t\tassert.Contains(t, err.Error(), \"*fx_test.A already decorated\")\n\t})\n\n\tt.Run(\"replace a value that wasn't provided\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\ttype A struct{}\n\n\t\tapp := NewForTest(t,\n\t\t\tfx.Replace(A{}),\n\t\t\tfx.Invoke(func(a *A) {\n\t\t\t}),\n\t\t)\n\t\terr := app.Err()\n\t\tassert.Error(t, err)\n\t\tassert.Contains(t, err.Error(), \"missing type: *fx_test.A\")\n\t})\n\n\tt.Run(\"replace panics on invalid values\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\ttype A struct{}\n\t\ttype B struct{}\n\n\t\trequire.PanicsWithValuef(\n\t\t\tt,\n\t\t\t\"untyped nil passed to fx.Replace\",\n\t\t\tfunc() { fx.Replace(A{}, nil) },\n\t\t\t\"a naked nil should panic\",\n\t\t)\n\n\t\trequire.PanicsWithValuef(\n\t\t\tt,\n\t\t\t\"error value passed to fx.Replace\",\n\t\t\tfunc() { fx.Replace(A{}, errors.New(\"some error\")) },\n\t\t\t\"replacing with an error should panic\",\n\t\t)\n\n\t\trequire.NotPanicsf(\n\t\t\tt,\n\t\t\tfunc() { fx.Replace(A{}, (*B)(nil)) },\n\t\t\t\"a wrapped nil should not panic\")\n\t})\n}\n"
  },
  {
    "path": "shutdown.go",
    "content": "// Copyright (c) 2019 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage fx\n\nimport (\n\t\"time\"\n)\n\n// Shutdowner provides a method that can manually trigger the shutdown of the\n// application by sending a signal to all open Done channels. Shutdowner works\n// on applications using Run as well as Start, Done, and Stop. The Shutdowner is\n// provided to all Fx applications.\ntype Shutdowner interface {\n\tShutdown(...ShutdownOption) error\n}\n\n// ShutdownOption provides a way to configure properties of the shutdown\n// process. Currently, no options have been implemented.\ntype ShutdownOption interface {\n\tapply(*shutdowner)\n}\n\ntype exitCodeOption int\n\nfunc (code exitCodeOption) apply(s *shutdowner) {\n\ts.exitCode = int(code)\n}\n\nvar _ ShutdownOption = exitCodeOption(0)\n\n// ExitCode is a [ShutdownOption] that may be passed to the Shutdown method of the\n// [Shutdowner] interface.\n// The given integer exit code will be broadcasted to any receiver waiting\n// on a [ShutdownSignal] from the [Wait] method.\nfunc ExitCode(code int) ShutdownOption {\n\treturn exitCodeOption(code)\n}\n\ntype shutdownTimeoutOption time.Duration\n\nfunc (shutdownTimeoutOption) apply(*shutdowner) {}\n\nvar _ ShutdownOption = shutdownTimeoutOption(0)\n\n// ShutdownTimeout is a [ShutdownOption] that allows users to specify a timeout\n// for a given call to Shutdown method of the [Shutdowner] interface. As the\n// Shutdown method will block while waiting for a signal receiver relay\n// goroutine to stop.\n//\n// Deprecated: This option has no effect. Shutdown is not a blocking operation.\nfunc ShutdownTimeout(timeout time.Duration) ShutdownOption {\n\treturn shutdownTimeoutOption(timeout)\n}\n\ntype shutdowner struct {\n\tapp      *App\n\texitCode int\n}\n\n// Shutdown broadcasts a signal to all of the application's Done channels\n// and begins the Stop process. Applications can be shut down only after they\n// have finished starting up.\nfunc (s *shutdowner) Shutdown(opts ...ShutdownOption) error {\n\tfor _, opt := range opts {\n\t\topt.apply(s)\n\t}\n\n\treturn s.app.receivers.b.Broadcast(ShutdownSignal{\n\t\tSignal:   _sigTERM,\n\t\tExitCode: s.exitCode,\n\t})\n}\n\nfunc (app *App) shutdowner() Shutdowner {\n\treturn &shutdowner{app: app}\n}\n"
  },
  {
    "path": "shutdown_test.go",
    "content": "// Copyright (c) 2019 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage fx_test\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"sync\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\t\"go.uber.org/fx\"\n\t\"go.uber.org/fx/fxtest\"\n)\n\nfunc TestShutdown(t *testing.T) {\n\tt.Parallel()\n\n\tt.Run(\"BroadcastsToMultipleChannels\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tvar s fx.Shutdowner\n\t\tapp := fxtest.New(\n\t\t\tt,\n\t\t\tfx.Populate(&s),\n\t\t)\n\n\t\tdone1, done2 := app.Done(), app.Done()\n\t\tdefer app.RequireStart().RequireStop()\n\n\t\tassert.NoError(t, s.Shutdown(), \"error in app shutdown\")\n\t\tassert.NotNil(t, <-done1, \"done channel 1 did not receive signal\")\n\t\tassert.NotNil(t, <-done2, \"done channel 2 did not receive signal\")\n\t})\n\n\tt.Run(\"ErrorOnUnsentSignal\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tvar s fx.Shutdowner\n\t\tapp := fxtest.New(\n\t\t\tt,\n\t\t\tfx.Populate(&s),\n\t\t)\n\n\t\tdone := app.Done()\n\t\twait := app.Wait()\n\t\tdefer app.RequireStart().RequireStop()\n\t\tassert.NoError(t, s.Shutdown(), \"error returned from first shutdown call\")\n\n\t\tassert.EqualError(t, s.Shutdown(), \"send terminated signal: 2/2 channels are blocked\",\n\t\t\t\"unexpected error returned when shutdown is called with a blocked channel\")\n\t\tassert.NotNil(t, <-done, \"done channel did not receive signal\")\n\t\tassert.NotNil(t, <-wait, \"wait channel did not receive signal\")\n\t})\n\n\tt.Run(\"shutdown app before calling Done()\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tvar s fx.Shutdowner\n\t\tapp := fxtest.New(\n\t\t\tt,\n\t\t\tfx.Populate(&s),\n\t\t)\n\n\t\trequire.NoError(t, app.Start(context.Background()), \"error starting app\")\n\t\tassert.NoError(t, s.Shutdown(), \"error in app shutdown\")\n\t\tdone1, done2 := app.Done(), app.Done()\n\t\tdefer app.Stop(context.Background())\n\t\t// Receiving on done1 and done2 will deadlock in the event that app.Done()\n\t\t// doesn't work as expected.\n\t\tassert.NotNil(t, <-done1, \"done channel 1 did not receive signal\")\n\t\tassert.NotNil(t, <-done2, \"done channel 2 did not receive signal\")\n\t})\n\n\tt.Run(\"with exit code\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tvar s fx.Shutdowner\n\t\tapp := fxtest.New(\n\t\t\tt,\n\t\t\tfx.Populate(&s),\n\t\t)\n\n\t\trequire.NoError(t, app.Start(context.Background()), \"error starting app\")\n\t\tassert.NoError(t, s.Shutdown(fx.ExitCode(2)), \"error in app shutdown\")\n\t\twait := <-app.Wait()\n\t\tdefer app.Stop(context.Background())\n\t\trequire.Equal(t, 2, wait.ExitCode)\n\t})\n\n\tt.Run(\"with exit code and multiple Wait\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tvar s fx.Shutdowner\n\t\tapp := fxtest.New(\n\t\t\tt,\n\t\t\tfx.Populate(&s),\n\t\t)\n\n\t\trequire.NoError(t, app.Start(context.Background()), \"error starting app\")\n\t\tt.Cleanup(func() { app.Stop(context.Background()) }) // in t.Cleanup so this happens after all subtests return (not just this function)\n\t\tdefer require.NoError(t, app.Stop(context.Background()))\n\n\t\tfor i := range 10 {\n\t\t\tt.Run(fmt.Sprintf(\"Wait %v\", i), func(t *testing.T) {\n\t\t\t\tt.Parallel()\n\t\t\t\twait := <-app.Wait()\n\t\t\t\trequire.Equal(t, 2, wait.ExitCode)\n\t\t\t})\n\t\t}\n\n\t\tassert.NoError(t, s.Shutdown(fx.ExitCode(2), fx.ShutdownTimeout(time.Second)))\n\t})\n\n\tt.Run(\"from invoke\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tapp := fxtest.New(\n\t\t\tt,\n\t\t\tfx.Invoke(func(s fx.Shutdowner) {\n\t\t\t\ts.Shutdown()\n\t\t\t}),\n\t\t)\n\n\t\tctx, cancel := context.WithTimeout(context.Background(), time.Second)\n\t\tdefer cancel()\n\n\t\trequire.NoError(t, app.Start(ctx), \"error starting app\")\n\t\tdefer app.Stop(ctx)\n\t\tselect {\n\t\tcase <-ctx.Done():\n\t\t\tassert.Fail(t, \"app did not shutdown in time\")\n\t\tcase <-app.Wait():\n\t\t\t// success\n\t\t}\n\t})\n\n\tt.Run(\"many times\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tvar shutdowner fx.Shutdowner\n\t\tapp := fxtest.New(\n\t\t\tt,\n\t\t\tfx.Populate(&shutdowner),\n\t\t)\n\n\t\tfor i := range 10 {\n\t\t\tapp.RequireStart()\n\t\t\tshutdowner.Shutdown(fx.ExitCode(i))\n\t\t\tassert.Equal(t, i, (<-app.Wait()).ExitCode, \"run %d\", i)\n\t\t\tapp.RequireStop()\n\t\t}\n\t})\n}\n\nfunc TestDataRace(t *testing.T) {\n\tt.Parallel()\n\n\tvar s fx.Shutdowner\n\tapp := fxtest.New(\n\t\tt,\n\t\tfx.Populate(&s),\n\t)\n\tdefer app.RequireStart().RequireStop()\n\n\tconst N = 50\n\tready := make(chan struct{}) // used to orchestrate goroutines for Done() and ShutdownOption()\n\tvar wg sync.WaitGroup        // tracks and waits for all goroutines\n\n\t// Spawn N goroutines, each of which call app.Done() and assert\n\t// the signal received.\n\twg.Add(N)\n\tfor i := range N {\n\t\ti := i\n\t\tgo func() {\n\t\t\tdefer wg.Done()\n\t\t\t<-ready\n\t\t\tdone := app.Done()\n\t\t\tassert.NotNil(t, <-done, \"done channel %v did not receive signal\", i)\n\t\t}()\n\t}\n\n\t// call Shutdown()\n\twg.Add(1)\n\tgo func() {\n\t\t<-ready\n\t\tdefer wg.Done()\n\t\tassert.NoError(t, s.Shutdown(), \"error in app shutdown\")\n\t}()\n\n\tclose(ready)\n\twg.Wait()\n}\n"
  },
  {
    "path": "signal.go",
    "content": "// Copyright (c) 2022 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage fx\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"os\"\n\t\"os/signal\"\n\t\"sync\"\n)\n\n// ShutdownSignal represents a signal to be written to Wait or Done.\n// Should a user call the Shutdown method via the Shutdowner interface with\n// a provided ExitCode, that exit code will be populated in the ExitCode field.\n//\n// Should the application receive an operating system signal,\n// the Signal field will be populated with the received os.Signal.\ntype ShutdownSignal struct {\n\tSignal   os.Signal\n\tExitCode int\n}\n\n// String will render a ShutdownSignal type as a string suitable for printing.\nfunc (sig ShutdownSignal) String() string {\n\treturn fmt.Sprintf(\"%v\", sig.Signal)\n}\n\nfunc newSignalReceivers() signalReceivers {\n\treturn signalReceivers{\n\t\tnotify:     signal.Notify,\n\t\tstopNotify: signal.Stop,\n\t\tsignals:    make(chan os.Signal, 1),\n\t\tb:          &broadcaster{},\n\t}\n}\n\n// signalReceivers listens to OS signals and shutdown signals,\n// and relays them to registered listeners when started.\ntype signalReceivers struct {\n\t// this mutex protects writes and reads of this struct to prevent\n\t// race conditions in a parallel execution pattern\n\tm sync.Mutex\n\n\t// our os.Signal channel we relay from\n\tsignals chan os.Signal\n\t// when written to, will instruct the signal relayer to shutdown\n\tshutdown chan struct{}\n\t// is written to when signal relay has finished shutting down\n\tfinished chan struct{}\n\n\t// this stub allows us to unit test signal relay functionality\n\tnotify     func(c chan<- os.Signal, sig ...os.Signal)\n\tstopNotify func(c chan<- os.Signal)\n\n\t// used to register and broadcast to signal listeners\n\t// created via Done and Wait\n\tb *broadcaster\n}\n\nfunc (recv *signalReceivers) relayer() {\n\tdefer func() {\n\t\trecv.finished <- struct{}{}\n\t}()\n\n\tselect {\n\tcase <-recv.shutdown:\n\t\treturn\n\tcase signal := <-recv.signals:\n\t\trecv.b.Broadcast(ShutdownSignal{\n\t\t\tSignal: signal,\n\t\t})\n\t}\n}\n\n// running returns true if the the signal relay go-routine is running.\n// this method must be invoked under locked mutex to avoid race condition.\nfunc (recv *signalReceivers) running() bool {\n\treturn recv.shutdown != nil && recv.finished != nil\n}\n\nfunc (recv *signalReceivers) Start() {\n\trecv.m.Lock()\n\tdefer recv.m.Unlock()\n\n\t// if the receiver has already been started; don't start it again\n\tif recv.running() {\n\t\treturn\n\t}\n\n\trecv.finished = make(chan struct{}, 1)\n\trecv.shutdown = make(chan struct{}, 1)\n\trecv.notify(recv.signals, os.Interrupt, _sigINT, _sigTERM)\n\tgo recv.relayer()\n}\n\nfunc (recv *signalReceivers) Stop(ctx context.Context) error {\n\trecv.m.Lock()\n\tdefer recv.m.Unlock()\n\trecv.stopNotify(recv.signals)\n\n\t// if the relayer is not running; return nil error\n\tif !recv.running() {\n\t\treturn nil\n\t}\n\n\trecv.shutdown <- struct{}{}\n\n\tselect {\n\tcase <-ctx.Done():\n\t\treturn ctx.Err()\n\tcase <-recv.finished:\n\t\tclose(recv.shutdown)\n\t\tclose(recv.finished)\n\t\trecv.shutdown = nil\n\t\trecv.finished = nil\n\t\trecv.b.reset()\n\t\treturn nil\n\t}\n}\n\nfunc (recv *signalReceivers) Done() <-chan os.Signal {\n\treturn recv.b.Done()\n}\n\nfunc (recv *signalReceivers) Wait() <-chan ShutdownSignal {\n\treturn recv.b.Wait()\n}\n"
  },
  {
    "path": "signal_test.go",
    "content": "// Copyright (c) 2022 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage fx\n\nimport (\n\t\"context\"\n\t\"os\"\n\t\"syscall\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc assertUnsentSignalError(\n\tt *testing.T,\n\terr error,\n\texpected *unsentSignalError,\n) {\n\tt.Helper()\n\n\tactual := new(unsentSignalError)\n\n\tassert.ErrorContains(t, err, \"channels are blocked\")\n\tif assert.ErrorAs(t, err, &actual, \"is unsentSignalError\") {\n\t\tassert.Equal(t, expected, actual)\n\t}\n}\n\nfunc TestSignal(t *testing.T) {\n\tt.Parallel()\n\tt.Run(\"Done\", func(t *testing.T) {\n\t\trecv := newSignalReceivers()\n\t\ta := recv.Done()\n\t\t_ = recv.Done() // we never listen on this\n\n\t\texpected := ShutdownSignal{\n\t\t\tSignal: syscall.SIGTERM,\n\t\t}\n\n\t\trequire.NoError(t, recv.b.Broadcast(expected), \"first broadcast should succeed\")\n\n\t\tassertUnsentSignalError(t, recv.b.Broadcast(expected), &unsentSignalError{\n\t\t\tSignal: expected,\n\t\t\tTotal:  2,\n\t\t\tUnsent: 2,\n\t\t})\n\n\t\tassert.Equal(t, expected.Signal, <-a)\n\t\tassert.Equal(t, expected.Signal, <-recv.Done(), \"expect cached signal\")\n\t})\n\n\tt.Run(\"signal notify relayer\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tt.Run(\"start and stop\", func(t *testing.T) {\n\t\t\tt.Parallel()\n\t\t\tt.Run(\"timeout\", func(t *testing.T) {\n\t\t\t\trecv := newSignalReceivers()\n\t\t\t\trecv.Start()\n\t\t\t\ttimeoutCtx, cancel := context.WithTimeout(context.Background(), 0)\n\t\t\t\tdefer cancel()\n\t\t\t\terr := recv.Stop(timeoutCtx)\n\t\t\t\trequire.ErrorIs(t, err, context.DeadlineExceeded)\n\t\t\t})\n\t\t\tt.Run(\"no error\", func(t *testing.T) {\n\t\t\t\trecv := newSignalReceivers()\n\t\t\t\tctx := t.Context()\n\t\t\t\trecv.Start()\n\t\t\t\trecv.Start() // should be a no-op if already running\n\t\t\t\trequire.NoError(t, recv.Stop(ctx))\n\t\t\t})\n\t\t\tt.Run(\"notify\", func(t *testing.T) {\n\t\t\t\tstub := make(chan os.Signal)\n\t\t\t\trecv := newSignalReceivers()\n\t\t\t\trecv.notify = func(ch chan<- os.Signal, _ ...os.Signal) {\n\t\t\t\t\tgo func() {\n\t\t\t\t\t\tfor sig := range stub {\n\t\t\t\t\t\t\tch <- sig\n\t\t\t\t\t\t}\n\t\t\t\t\t}()\n\t\t\t\t}\n\t\t\t\tvar stopCalledTimes int\n\t\t\t\trecv.stopNotify = func(ch chan<- os.Signal) {\n\t\t\t\t\tstopCalledTimes++\n\t\t\t\t}\n\t\t\t\tctx := t.Context()\n\t\t\t\trecv.Start()\n\t\t\t\tstub <- syscall.SIGTERM\n\t\t\t\tstub <- syscall.SIGTERM\n\t\t\t\trequire.Equal(t, syscall.SIGTERM, <-recv.Done())\n\t\t\t\trequire.Equal(t, syscall.SIGTERM, <-recv.Done())\n\t\t\t\tsig := <-recv.Wait()\n\t\t\t\trequire.Equal(t, syscall.SIGTERM, sig.Signal)\n\t\t\t\trequire.NoError(t, recv.Stop(ctx))\n\t\t\t\trequire.Equal(t, 1, stopCalledTimes)\n\t\t\t\tclose(stub)\n\t\t\t})\n\t\t})\n\t})\n\n\tt.Run(\"stop deadlock\", func(t *testing.T) {\n\t\trecv := newSignalReceivers()\n\n\t\tvar notify chan<- os.Signal\n\t\trecv.notify = func(ch chan<- os.Signal, _ ...os.Signal) {\n\t\t\tnotify = ch\n\t\t}\n\t\trecv.Start()\n\n\t\t// Artificially create a race where the relayer receives an OS signal\n\t\t// while Stop() holds the lock. If this leads to deadlock,\n\t\t// we will receive a context timeout error.\n\t\tgotErr := make(chan error, 1)\n\t\tnotify <- syscall.SIGTERM\n\t\tgo func() {\n\t\t\tstopCtx, cancel := context.WithTimeout(context.Background(), time.Second)\n\t\t\tdefer cancel()\n\t\t\tgotErr <- recv.Stop(stopCtx)\n\t\t}()\n\t\tassert.NoError(t, <-gotErr)\n\t})\n}\n"
  },
  {
    "path": "supply.go",
    "content": "// Copyright (c) 2020 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage fx\n\nimport (\n\t\"fmt\"\n\t\"reflect\"\n\t\"strings\"\n\n\t\"go.uber.org/fx/internal/fxreflect\"\n)\n\n// Supply provides instantiated values for dependency injection as if\n// they had been provided using a constructor that simply returns them.\n// The most specific type of each value (as determined by reflection) is used.\n//\n// This serves a purpose similar to what fx.Replace does for fx.Decorate.\n//\n// For example, given:\n//\n//\ttype (\n//\t\tTypeA struct{}\n//\t\tTypeB struct{}\n//\t\tTypeC struct{}\n//\t)\n//\n//\tvar a, b, c = &TypeA{}, TypeB{}, &TypeC{}\n//\n// The following two forms are equivalent:\n//\n//\tfx.Supply(a, b, fx.Annotated{Target: c})\n//\n//\tfx.Provide(\n//\t\tfunc() *TypeA { return a },\n//\t\tfunc() TypeB { return b },\n//\t\tfx.Annotated{Target: func() *TypeC { return c }},\n//\t)\n//\n// Supply panics if a value (or annotation target) is an untyped nil or an error.\n//\n// [Private] can be used to restrict access to supplied values.\n//\n// # Supply Caveats\n//\n// As mentioned above, Supply uses the most specific type of the provided\n// value. For interface values, this refers to the type of the implementation,\n// not the interface. So if you supply an http.Handler, fx.Supply will use the\n// type of the implementation.\n//\n//\tvar handler http.Handler = http.HandlerFunc(f)\n//\tfx.Supply(handler)\n//\n// Is equivalent to,\n//\n//\tfx.Provide(func() http.HandlerFunc { return f })\n//\n// This is typically NOT what you intended. To supply the handler above as an\n// http.Handler, we need to use the fx.Annotate function with the fx.As\n// annotation.\n//\n//\tfx.Supply(\n//\t\tfx.Annotate(handler, fx.As(new(http.Handler))),\n//\t)\nfunc Supply(values ...any) Option {\n\tconstructors := make([]any, 0, len(values))\n\ttypes := make([]reflect.Type, 0, len(values))\n\tvar private bool\n\tfor _, value := range values {\n\t\tvar (\n\t\t\ttyp  reflect.Type\n\t\t\tctor any\n\t\t)\n\t\tswitch value := value.(type) {\n\t\tcase privateOption:\n\t\t\tprivate = true\n\t\t\tcontinue\n\t\tcase annotated:\n\t\t\tvalue.Target, typ = newSupplyConstructor(value.Target)\n\t\t\tctor = value\n\t\tcase Annotated:\n\t\t\tvalue.Target, typ = newSupplyConstructor(value.Target)\n\t\t\tctor = value\n\t\tdefault:\n\t\t\tctor, typ = newSupplyConstructor(value)\n\t\t}\n\t\tconstructors = append(constructors, ctor)\n\t\ttypes = append(types, typ)\n\t}\n\n\treturn supplyOption{\n\t\tTargets: constructors,\n\t\tTypes:   types,\n\t\tStack:   fxreflect.CallerStack(1, 0),\n\t\tPrivate: private,\n\t}\n}\n\ntype supplyOption struct {\n\tTargets []any\n\tTypes   []reflect.Type // type of value produced by constructor[i]\n\tStack   fxreflect.Stack\n\tPrivate bool\n}\n\nfunc (o supplyOption) apply(m *module) {\n\tfor i, target := range o.Targets {\n\t\tm.provides = append(m.provides, provide{\n\t\t\tTarget:     target,\n\t\t\tStack:      o.Stack,\n\t\t\tIsSupply:   true,\n\t\t\tSupplyType: o.Types[i],\n\t\t\tPrivate:    o.Private,\n\t\t})\n\t}\n}\n\nfunc (o supplyOption) String() string {\n\titems := make([]string, 0, len(o.Targets))\n\tfor _, typ := range o.Types {\n\t\titems = append(items, typ.String())\n\t}\n\treturn fmt.Sprintf(\"fx.Supply(%s)\", strings.Join(items, \", \"))\n}\n\n// Returns a function that takes no parameters, and returns the given value.\nfunc newSupplyConstructor(value any) (any, reflect.Type) {\n\tswitch value.(type) {\n\tcase nil:\n\t\tpanic(\"untyped nil passed to fx.Supply\")\n\tcase error:\n\t\tpanic(\"error value passed to fx.Supply\")\n\t}\n\n\ttyp := reflect.TypeOf(value)\n\treturnTypes := []reflect.Type{typ}\n\treturnValues := []reflect.Value{reflect.ValueOf(value)}\n\n\tft := reflect.FuncOf([]reflect.Type{}, returnTypes, false)\n\tfv := reflect.MakeFunc(ft, func([]reflect.Value) []reflect.Value {\n\t\treturn returnValues\n\t})\n\n\treturn fv.Interface(), typ\n}\n"
  },
  {
    "path": "supply_test.go",
    "content": "// Copyright (c) 2020 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage fx_test\n\nimport (\n\t\"bytes\"\n\t\"errors\"\n\t\"io\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\n\t\"go.uber.org/fx\"\n\t\"go.uber.org/fx/fxevent\"\n\t\"go.uber.org/fx/fxtest\"\n\t\"go.uber.org/fx/internal/fxlog\"\n)\n\nfunc TestSupply(t *testing.T) {\n\tt.Parallel()\n\n\ttype A struct{}\n\ttype B struct{}\n\n\tt.Run(\"NothingIsSupplied\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tapp := fxtest.New(t, fx.Supply())\n\t\tdefer app.RequireStart().RequireStop()\n\t})\n\n\tt.Run(\"SomethingIsSupplied\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\taIn, bIn := &A{}, &B{}\n\t\tvar aOut *A\n\t\tvar bOut *B\n\n\t\tapp := fxtest.New(\n\t\t\tt,\n\t\t\tfx.Supply(aIn, bIn),\n\t\t\tfx.Populate(&aOut, &bOut),\n\t\t)\n\t\tdefer app.RequireStart().RequireStop()\n\n\t\trequire.Same(t, aIn, aOut)\n\t\trequire.Same(t, bIn, bOut)\n\t})\n\n\tt.Run(\"SupplyInModule\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\taIn, bIn := &A{}, &B{}\n\t\tvar aOut *A\n\t\tvar bOut *B\n\n\t\tapp := fxtest.New(\n\t\t\tt,\n\t\t\tfx.Module(\"module\",\n\t\t\t\tfx.Supply(aIn, bIn),\n\t\t\t),\n\t\t\tfx.Populate(&aOut, &bOut),\n\t\t)\n\t\tdefer app.RequireStart().RequireStop()\n\t\trequire.Same(t, aIn, aOut)\n\t\trequire.Same(t, bIn, bOut)\n\t})\n\n\tt.Run(\"AnnotateIsSupplied\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tfirstIn, secondIn, thirdIn := &A{}, &A{}, &B{}\n\t\tvar out struct {\n\t\t\tfx.In\n\n\t\t\tFirst  *A `name:\"first\"`\n\t\t\tSecond *A `name:\"second\"`\n\t\t\tThird  *B\n\t\t}\n\n\t\tapp := fxtest.New(\n\t\t\tt,\n\t\t\tfx.Supply(\n\t\t\t\tfx.Annotated{Name: \"first\", Target: firstIn},\n\t\t\t\tfx.Annotated{Name: \"second\", Target: secondIn},\n\t\t\t\tthirdIn),\n\t\t\tfx.Populate(&out),\n\t\t)\n\t\tdefer app.RequireStart().RequireStop()\n\n\t\trequire.Same(t, firstIn, out.First)\n\t\trequire.Same(t, secondIn, out.Second)\n\t\trequire.Same(t, thirdIn, out.Third)\n\t})\n\n\tt.Run(\"AnnotateIsSupported\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tvar out struct {\n\t\t\tfx.In\n\n\t\t\tGot io.Writer\n\t\t}\n\n\t\tvar give bytes.Buffer\n\t\tapp := fxtest.New(t,\n\t\t\tfx.Supply(fx.Annotate(&give, fx.As(new(io.Writer)))),\n\t\t\tfx.Populate(&out),\n\t\t)\n\t\tdefer app.RequireStart().RequireStop()\n\n\t\trequire.Same(t, &give, out.Got)\n\t})\n\n\tt.Run(\"InvalidArgumentIsSupplied\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\trequire.PanicsWithValuef(\n\t\t\tt,\n\t\t\t\"untyped nil passed to fx.Supply\",\n\t\t\tfunc() { fx.Supply(A{}, nil) },\n\t\t\t\"a naked nil should panic\",\n\t\t)\n\n\t\trequire.NotPanicsf(\n\t\t\tt,\n\t\t\tfunc() { fx.Supply(A{}, (*B)(nil)) },\n\t\t\t\"a wrapped nil should not panic\")\n\n\t\trequire.PanicsWithValuef(\n\t\t\tt,\n\t\t\t\"error value passed to fx.Supply\",\n\t\t\tfunc() { fx.Supply(A{}, errors.New(\"fail\")) },\n\t\t\t\"an error value should panic\",\n\t\t)\n\t})\n\n\tt.Run(\"SupplyCollision\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\ttype foo struct{}\n\n\t\tvar spy fxlog.Spy\n\t\tapp := fx.New(\n\t\t\tfx.WithLogger(func() fxevent.Logger { return &spy }),\n\t\t\tfx.Supply(&foo{}, &foo{}),\n\t\t)\n\n\t\trequire.Error(t, app.Err())\n\t\tassert.Contains(t, app.Err().Error(), \"already provided\")\n\n\t\tsupplied := spy.Events().SelectByTypeName(\"Supplied\")\n\t\trequire.Len(t, supplied, 2)\n\t\trequire.NoError(t, supplied[0].(*fxevent.Supplied).Err)\n\t\trequire.Error(t, supplied[1].(*fxevent.Supplied).Err)\n\t})\n\n\tt.Run(\"SupplyToASoftGroup\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\ttype Param struct {\n\t\t\tfx.In\n\n\t\t\tFoos []string `group:\"foo,soft\"`\n\t\t\tBar  []int    `group:\"bar\"`\n\t\t}\n\t\ttype Result struct {\n\t\t\tfx.Out\n\n\t\t\tFoo string `group:\"foo\"`\n\t\t\tBar int    `group:\"bar\"`\n\t\t}\n\t\tapp := fxtest.New(t,\n\t\t\tfx.Supply(\n\t\t\t\tResult{\n\t\t\t\t\tFoo: \"sad\",\n\t\t\t\t\tBar: 20,\n\t\t\t\t}),\n\t\t\tfx.Supply(\n\t\t\t\tfx.Annotated{\n\t\t\t\t\tTarget: 10,\n\t\t\t\t\tGroup:  \"bar\",\n\t\t\t\t},\n\t\t\t\tfx.Annotated{\n\t\t\t\t\tTarget: \"bye\",\n\t\t\t\t\tGroup:  \"foo\",\n\t\t\t\t}),\n\t\t\tfx.Supply(fx.Annotated{\n\t\t\t\tTarget: \"hello\",\n\t\t\t\tGroup:  \"foo\",\n\t\t\t}),\n\t\t\tfx.Invoke(func(p Param) {\n\t\t\t\tassert.ElementsMatch(t, []string{\"sad\"}, p.Foos)\n\t\t\t}),\n\t\t)\n\n\t\tdefer app.RequireStart().RequireStop()\n\t})\n}\n"
  },
  {
    "path": "tools/analysis/passes/allfxevents/analysis.go",
    "content": "// Copyright (c) 2021 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\n// Package allfxevents implements a Go analysis pass that verifies that an\n// fxevent.Logger implementation handles all known fxevent types. As a special\n// case for no-op or fake fxevent.Loggers, it ignores implementations that\n// handle none of the event types.\n//\n// This is meant for use within Fx only.\npackage allfxevents\n\nimport (\n\t\"fmt\"\n\t\"go/ast\"\n\t\"go/token\"\n\t\"go/types\"\n\t\"sort\"\n\t\"strings\"\n\n\t\"golang.org/x/tools/go/analysis\"\n\t\"golang.org/x/tools/go/analysis/passes/inspect\"\n\t\"golang.org/x/tools/go/ast/inspector\"\n\t\"golang.org/x/tools/go/types/typeutil\"\n)\n\n// Analyzer is a go/analysis compatible analyzer that verifies that all\n// fxevent.Loggers shipped with Fx handle all known Fx event types.\nvar Analyzer = &analysis.Analyzer{\n\tName: \"allfxevents\",\n\tDoc:  \"check for unhandled fxevent.Events\",\n\tRun:  run,\n\tRequires: []*analysis.Analyzer{\n\t\tinspect.Analyzer,\n\t},\n}\n\nvar _filter = []ast.Node{\n\t&ast.File{},\n\t&ast.FuncDecl{},\n\t&ast.CaseClause{},\n\t&ast.TypeAssertExpr{},\n}\n\nfunc run(pass *analysis.Pass) (interface{}, error) {\n\tfxeventPkg, ok := findPackage(pass.Pkg, \"go.uber.org/fx/fxevent\")\n\tif !ok {\n\t\t// If the package doesn't import fxevent, and itself isn't\n\t\t// fxevent, then we don't need to run this pass.\n\t\treturn nil, nil\n\t}\n\n\tv := visitor{\n\t\tFxevent: inspectFxevent(fxeventPkg),\n\t\tFset:    pass.Fset,\n\t\tInfo:    pass.TypesInfo,\n\t\tReport:  pass.Report,\n\t}\n\n\tpass.ResultOf[inspect.Analyzer].(*inspector.Inspector).Nodes(_filter, v.Visit)\n\treturn nil, nil\n}\n\ntype visitor struct {\n\tFset    *token.FileSet\n\tInfo    *types.Info\n\tFxevent fxevent\n\tReport  func(analysis.Diagnostic)\n\n\t// types not yet referenced by this function\n\tloggerType types.Type\n\tfuncEvents *typeSet\n}\n\nfunc (v *visitor) Visit(n ast.Node, push bool) (recurse bool) {\n\tswitch n := n.(type) {\n\tcase *ast.File:\n\t\tif !push {\n\t\t\treturn false\n\t\t}\n\n\t\t// Don't run the linter on test files.\n\t\tfname := v.Fset.File(n.Pos()).Name()\n\t\treturn !strings.HasSuffix(fname, \"_test.go\")\n\n\tcase *ast.FuncDecl:\n\t\tif !push {\n\t\t\treturn v.funcDeclExit(n)\n\t\t}\n\t\treturn v.funcDeclEnter(n)\n\n\tcase *ast.CaseClause:\n\t\tif !push {\n\t\t\treturn false\n\t\t}\n\t\tfor _, expr := range n.List {\n\t\t\tt := v.Info.Types[expr].Type\n\t\t\tif t != nil {\n\t\t\t\tv.funcEvents.Remove(t)\n\t\t\t}\n\t\t}\n\n\tcase *ast.TypeAssertExpr:\n\t\tif !push {\n\t\t\treturn false\n\t\t}\n\t\tt := v.Info.Types[n.Type].Type\n\t\tif t != nil {\n\t\t\tv.funcEvents.Remove(t)\n\t\t}\n\t}\n\n\treturn false\n}\n\nfunc (v *visitor) funcDeclEnter(n *ast.FuncDecl) bool {\n\t// Skip top-level functions, and methods not named\n\t// LogEvent.\n\tif n.Recv == nil || n.Name.Name != \"LogEvent\" {\n\t\treturn false\n\t}\n\n\t// Skip types that don't implement fxevent.Logger.\n\tt := v.Info.Types[n.Recv.List[0].Type].Type\n\tif t == nil || !types.Implements(t, v.Fxevent.LoggerInterface) {\n\t\treturn false\n\t}\n\n\t// Each function declaration gets its own copy of the typeSet to track\n\t// events in.\n\tv.loggerType = t\n\tv.funcEvents = v.Fxevent.Events.Clone()\n\treturn true\n}\n\nfunc (v *visitor) funcDeclExit(n *ast.FuncDecl) bool {\n\tnEvents := v.funcEvents.Len()\n\tif nEvents == 0 {\n\t\treturn false\n\t}\n\n\t// If the logger doesn't handle *any* event type, it's probably a fake,\n\t// or a no-op implementation. Don't bother with it.\n\tif nEvents == v.Fxevent.Events.Len() {\n\t\treturn false\n\t}\n\n\tmissing := make([]string, 0, nEvents)\n\tv.funcEvents.Iterate(func(t types.Type) {\n\t\t// Use a fxevent qualifier so that event names don't include\n\t\t// the full import path of the fxevent package.\n\t\tmissing = append(missing, types.TypeString(t, emptyQualifier))\n\t})\n\tsort.Strings(missing)\n\n\tv.Report(analysis.Diagnostic{\n\t\tPos: n.Pos(),\n\t\tMessage: fmt.Sprintf(\"%v doesn't handle %v\",\n\t\t\ttypes.TypeString(v.loggerType, emptyQualifier),\n\t\t\tmissing,\n\t\t),\n\t})\n\n\treturn false\n}\n\n// Find the package with the given import path.\nfunc findPackage(pkg *types.Package, importPath string) (_ *types.Package, ok bool) {\n\tif pkg.Path() == importPath {\n\t\treturn pkg, true\n\t}\n\n\tfor _, imp := range pkg.Imports() {\n\t\tif imp.Path() == importPath {\n\t\t\treturn imp, true\n\t\t}\n\t}\n\n\treturn nil, false\n}\n\n// fxevent holds type information extracted from the fxevent package necessary\n// for inspection.\ntype fxevent struct {\n\tLogger          types.Type       // fxevent.Logger\n\tLoggerInterface *types.Interface // raw type information for fxevent.Logger\n\n\tEvent  types.Type // fxevent.Type\n\tEvents typeSet\n}\n\nfunc inspectFxevent(pkg *types.Package) fxevent {\n\tscope := pkg.Scope()\n\tevent := scope.Lookup(\"Event\").Type()\n\n\tvar eventTypes typeSet\n\tfor _, name := range scope.Names() {\n\t\tif name == \"Event\" {\n\t\t\tcontinue\n\t\t}\n\n\t\tobj := scope.Lookup(name)\n\t\tif !obj.Exported() {\n\t\t\tcontinue\n\t\t}\n\n\t\ttyp := obj.Type()\n\n\t\tif !types.ConvertibleTo(typ, event) {\n\t\t\ttyp = types.NewPointer(typ)\n\t\t\tif !types.ConvertibleTo(typ, event) {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t}\n\n\t\teventTypes.Put(typ)\n\t}\n\n\tlogger := scope.Lookup(\"Logger\").Type()\n\treturn fxevent{\n\t\tLogger:          logger,\n\t\tLoggerInterface: logger.Underlying().(*types.Interface),\n\t\tEvent:           event,\n\t\tEvents:          eventTypes,\n\t}\n}\n\n// A set of types.Type objects. The zero value is valid.\ntype typeSet struct{ m typeutil.Map }\n\nfunc (ts *typeSet) Len() int {\n\treturn ts.m.Len()\n}\n\n// Put puts an item into the set.\nfunc (ts *typeSet) Put(t types.Type) {\n\tts.m.Set(t, struct{}{})\n}\n\n// Remove removes an item from the set, reporting whether it was found in the\n// set.\nfunc (ts *typeSet) Remove(t types.Type) (found bool) {\n\treturn ts.m.Delete(t)\n}\n\n// Iterate iterates through the type set in an unspecified order.\nfunc (ts *typeSet) Iterate(f func(types.Type)) {\n\tts.m.Iterate(func(t types.Type, _ interface{}) {\n\t\tf(t)\n\t})\n}\n\nfunc (ts *typeSet) Clone() *typeSet {\n\tvar out typeSet\n\tts.Iterate(out.Put)\n\treturn &out\n}\n\n// Use this as a types.Qualifier to print the name of an entity with\n// types.TypeString or similar without including their full package path.\nfunc emptyQualifier(*types.Package) string {\n\treturn \"\"\n}\n"
  },
  {
    "path": "tools/analysis/passes/allfxevents/analysis_test.go",
    "content": "// Copyright (c) 2021 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage allfxevents\n\nimport (\n\t\"testing\"\n\n\t\"golang.org/x/tools/go/analysis/analysistest\"\n)\n\nfunc TestAnalyzer(t *testing.T) {\n\tt.Parallel()\n\n\tanalysistest.Run(t, analysistest.TestData(), Analyzer, \"./...\")\n}\n"
  },
  {
    "path": "tools/analysis/passes/allfxevents/testdata/src/a/full.go",
    "content": "package a\n\nimport (\n\t\"fmt\"\n\n\t\"go.uber.org/fx/fxevent\"\n)\n\ntype fullLogger struct{}\n\nfunc (*fullLogger) LogEvent(ev fxevent.Event) {\n\tswitch ev.(type) {\n\tcase *fxevent.Foo, *fxevent.Bar, *fxevent.Baz:\n\t\tfmt.Println(ev)\n\tcase *fxevent.Qux:\n\t\tfmt.Println(ev)\n\t}\n}\n"
  },
  {
    "path": "tools/analysis/passes/allfxevents/testdata/src/a/nop.go",
    "content": "package a\n\nimport \"go.uber.org/fx/fxevent\"\n\ntype nopLogger struct{}\n\nfunc (nopLogger) LogEvent(fxevent.Event) {\n\t// Don't do anything with the event. Should not cause a\n\t// diagnostic.\n}\n"
  },
  {
    "path": "tools/analysis/passes/allfxevents/testdata/src/a/not_a_logger.go",
    "content": "package a\n\nimport (\n\t\"fmt\"\n\n\t\"go.uber.org/fx/fxevent\"\n)\n\ntype notALogger struct{}\n\n// Doesn't implement fxevent.Logger because it returns an error. This shouldn't\n// cause a diagnostic.\n\nfunc (*notALogger) LogEvent(ev fxevent.Event) error {\n\t_, ok := ev.(*fxevent.Foo)\n\tfmt.Println(ok)\n\treturn nil\n}\n"
  },
  {
    "path": "tools/analysis/passes/allfxevents/testdata/src/a/partial_test.go",
    "content": "package a\n\nimport (\n\t\"fmt\"\n\n\t\"go.uber.org/fx/fxevent\"\n)\n\n// This logger intentionally doesn't handle everything. We don't expect any\n// diagnostics reported for it because it's in a test file.\ntype partialLogger struct{}\n\nfunc (partialLogger) LogEvent(ev fxevent.Event) {\n\t_, ok := ev.(*fxevent.Foo)\n\tfmt.Println(ok)\n}\n"
  },
  {
    "path": "tools/analysis/passes/allfxevents/testdata/src/a/ptr.go",
    "content": "package a\n\nimport (\n\t\"log\"\n\n\t\"go.uber.org/fx/fxevent\"\n)\n\ntype ptrLogger struct{}\n\nfunc (*ptrLogger) LogEvent(ev fxevent.Event) { // want `\\*ptrLogger doesn't handle \\[\\*Bar \\*Foo\\]`\n\tif e, ok := ev.(*fxevent.Baz); ok {\n\t\tlog.Print(e)\n\t}\n\n\tif e, ok := ev.(*fxevent.Qux); ok {\n\t\tlog.Print(e)\n\t}\n}\n"
  },
  {
    "path": "tools/analysis/passes/allfxevents/testdata/src/a/value.go",
    "content": "package a\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\n\t\"go.uber.org/fx/fxevent\"\n)\n\ntype valueLogger struct {\n\tW io.Writer\n}\n\nfunc (l valueLogger) LogEvent(ev fxevent.Event) { // want `valueLogger doesn't handle \\[\\*Baz \\*Qux\\]`\n\tswitch ev.(type) {\n\tcase *fxevent.Foo:\n\t\tfmt.Fprintln(l.W, ev)\n\tcase *fxevent.Bar:\n\t\tfmt.Fprintln(l.W, ev)\n\t}\n}\n"
  },
  {
    "path": "tools/analysis/passes/allfxevents/testdata/src/b/fxevent/logger.go",
    "content": "package fxevent\n\ntype (\n\tEvent  struct{}\n\tLogger interface{ LogEvent(Event) }\n)\n"
  },
  {
    "path": "tools/analysis/passes/allfxevents/testdata/src/b/not_real_fxevent.go",
    "content": "package b\n\nimport (\n\t\"b/fxevent\"\n\t\"fmt\"\n)\n\ntype Logger struct{}\n\nvar _ fxevent.Logger = Logger{}\n\nfunc (Logger) LogEvent(ev fxevent.Event) {\n\tfmt.Println(ev)\n}\n"
  },
  {
    "path": "tools/analysis/passes/allfxevents/testdata/src/go.uber.org/fx/fxevent/fxevent.go",
    "content": "package fxevent\n\n// This is a partial fxevent package inspired by the real fxevent package,\n// but with a fixed list of events we can test against.\n\ntype (\n\tLogger interface{ LogEvent(Event) }\n\tEvent  interface{ event() }\n\tFoo    struct{}\n\tBar    struct{}\n\tBaz    struct{}\n\tQux    struct{}\n)\n\nfunc (*Foo) event() {}\nfunc (*Bar) event() {}\nfunc (*Baz) event() {}\nfunc (*Qux) event() {}\n"
  },
  {
    "path": "tools/analysis/passes/allfxevents/testdata/src/go.uber.org/fx/fxevent/partial.go",
    "content": "package fxevent\n\n// Partial logger implementation in the same package as fxevent.Logger.\ntype partialLogger struct{}\n\nfunc (partialLogger) LogEvent(ev Event) { // want `partialLogger doesn't handle \\[\\*Qux\\]`\n\tswitch ev.(type) {\n\tcase *Foo:\n\tcase *Bar:\n\tcase *Baz:\n\t}\n}\n"
  },
  {
    "path": "tools/cmd/fxlint/main.go",
    "content": "// Copyright (c) 2021 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\n// fxlint is a linter that verifies correct usage of Fx.\n//\n// Currently, the following passes are provided:\n//\n// - allfxevents: Verifies that all Fx events are handled by an fxevent.Logger.\npackage main\n\nimport (\n\t\"go.uber.org/fx/tools/analysis/passes/allfxevents\"\n\t\"golang.org/x/tools/go/analysis/multichecker\"\n)\n\nfunc main() {\n\tmultichecker.Main(\n\t\tallfxevents.Analyzer,\n\t)\n}\n"
  },
  {
    "path": "tools/go.mod",
    "content": "module go.uber.org/fx/tools\n\ngo 1.22.0\n\ntoolchain go1.24.0\n\nrequire golang.org/x/tools v0.30.0\n\nrequire (\n\tgolang.org/x/mod v0.23.0 // indirect\n\tgolang.org/x/sync v0.11.0 // indirect\n)\n"
  },
  {
    "path": "tools/go.sum",
    "content": "github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=\ngithub.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=\ngolang.org/x/mod v0.23.0 h1:Zb7khfcRGKk+kqfxFaP5tZqCnDZMjC5VtUBs87Hr6QM=\ngolang.org/x/mod v0.23.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=\ngolang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w=\ngolang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=\ngolang.org/x/tools v0.30.0 h1:BgcpHewrV5AUp2G9MebG4XPFI1E2W41zU1SaqVA9vJY=\ngolang.org/x/tools v0.30.0/go.mod h1:c347cR/OJfw5TI+GfX7RUPNMdDRRbjvYTS0jPyvsVtY=\n"
  },
  {
    "path": "version.go",
    "content": "// Copyright (c) 2019 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage fx\n\n// Version is exported for runtime compatibility checks.\nconst Version = \"1.25.0-dev\"\n"
  }
]