Full Code of uber-go/fx for AI

master d5da5b04ac90 cached
191 files
770.9 KB
215.8k tokens
771 symbols
1 requests
Download .txt
Showing preview only (821K chars total). Download the full file or copy to clipboard to get everything.
Repository: uber-go/fx
Branch: master
Commit: d5da5b04ac90
Files: 191
Total size: 770.9 KB

Directory structure:
gitextract_i617t6c0/

├── .codecov.yml
├── .github/
│   ├── ISSUE_TEMPLATE/
│   │   ├── bug_report.md
│   │   ├── config.yml
│   │   └── feature_request.md
│   ├── dependabot.yml
│   └── workflows/
│       ├── docs.yml
│       ├── fossa.yaml
│       └── go.yml
├── .gitignore
├── .golangci.yml
├── CHANGELOG.md
├── CONTRIBUTING.md
├── LICENSE
├── Makefile
├── README.md
├── annotated.go
├── annotated_test.go
├── app.go
├── app_internal_test.go
├── app_test.go
├── app_unixes.go
├── app_wasm.go
├── app_windows.go
├── app_windows_test.go
├── broadcast.go
├── decorate.go
├── decorate_test.go
├── doc.go
├── docs/
│   ├── .gitattributes
│   ├── .gitignore
│   ├── Makefile
│   ├── ex/
│   │   ├── annotate/
│   │   │   ├── cast.go
│   │   │   ├── cast_bad.go
│   │   │   ├── cast_test.go
│   │   │   ├── github/
│   │   │   │   └── stub.go
│   │   │   ├── sample.go
│   │   │   └── sample_test.go
│   │   ├── get-started/
│   │   │   ├── 01-minimal/
│   │   │   │   ├── main.go
│   │   │   │   └── main_test.go
│   │   │   ├── 02-http-server/
│   │   │   │   ├── main.go
│   │   │   │   └── main_test.go
│   │   │   ├── 03-echo-handler/
│   │   │   │   ├── main.go
│   │   │   │   └── main_test.go
│   │   │   ├── 04-logger/
│   │   │   │   ├── main.go
│   │   │   │   └── main_test.go
│   │   │   ├── 05-registration/
│   │   │   │   ├── main.go
│   │   │   │   └── main_test.go
│   │   │   ├── 06-another-handler/
│   │   │   │   ├── main.go
│   │   │   │   └── main_test.go
│   │   │   └── 07-many-handlers/
│   │   │       ├── main.go
│   │   │       └── main_test.go
│   │   ├── modules/
│   │   │   ├── module.go
│   │   │   └── module_test.go
│   │   ├── parameter-objects/
│   │   │   ├── define.go
│   │   │   ├── define_test.go
│   │   │   ├── extend.go
│   │   │   └── extend_test.go
│   │   ├── result-objects/
│   │   │   ├── define.go
│   │   │   ├── define_test.go
│   │   │   ├── extend.go
│   │   │   └── extend_test.go
│   │   └── value-groups/
│   │       ├── consume/
│   │       │   ├── annotate.go
│   │       │   ├── consume_test.go
│   │       │   └── param.go
│   │       └── feed/
│   │           ├── annotate.go
│   │           ├── feed_test.go
│   │           └── result.go
│   ├── go.mod
│   ├── go.sum
│   ├── internal/
│   │   ├── apptest/
│   │   │   ├── run.go
│   │   │   └── run_test.go
│   │   ├── exectest/
│   │   │   ├── cmd.go
│   │   │   ├── cmd_test.go
│   │   │   ├── output.go
│   │   │   └── output_test.go
│   │   ├── httptest/
│   │   │   ├── http.go
│   │   │   └── http_test.go
│   │   ├── iotest/
│   │   │   ├── read.go
│   │   │   └── read_test.go
│   │   └── test/
│   │       ├── fake.go
│   │       ├── fake_test.go
│   │       ├── t.go
│   │       └── t_test.go
│   ├── mkdocs.yml
│   ├── pyproject.toml
│   └── src/
│       ├── annotate.md
│       ├── container.md
│       ├── faq.md
│       ├── get-started/
│       │   ├── another-handler.md
│       │   ├── conclusion.md
│       │   ├── echo-handler.md
│       │   ├── http-server.md
│       │   ├── index.md
│       │   ├── logger.md
│       │   ├── many-handlers.md
│       │   ├── minimal.md
│       │   └── registration.md
│       ├── index.md
│       ├── lifecycle.md
│       ├── modules.md
│       ├── parameter-objects.md
│       ├── result-objects.md
│       └── value-groups/
│           ├── consume.md
│           ├── feed.md
│           └── index.md
├── error_example_test.go
├── example_test.go
├── extract.go
├── extract_test.go
├── fxevent/
│   ├── console.go
│   ├── console_test.go
│   ├── doc.go
│   ├── event.go
│   ├── event_test.go
│   ├── logger.go
│   ├── slog.go
│   ├── slog_test.go
│   ├── zap.go
│   └── zap_test.go
├── fxtest/
│   ├── app.go
│   ├── app_test.go
│   ├── doc.go
│   ├── lifecycle.go
│   ├── lifecycle_test.go
│   ├── printer.go
│   ├── printer_test.go
│   ├── tb.go
│   └── tb_test.go
├── go.mod
├── go.sum
├── inout.go
├── inout_test.go
├── internal/
│   ├── e2e/
│   │   ├── README.md
│   │   ├── go.mod
│   │   ├── go.sum
│   │   ├── shutdowner_run_exitcode/
│   │   │   ├── main.go
│   │   │   └── main_test.go
│   │   └── shutdowner_wait_exitcode/
│   │       ├── main.go
│   │       └── main_test.go
│   ├── fxclock/
│   │   ├── clock.go
│   │   └── clock_test.go
│   ├── fxlog/
│   │   ├── default.go
│   │   ├── default_test.go
│   │   ├── foovendor/
│   │   │   └── foovendor.go
│   │   ├── sample.git/
│   │   │   └── sample.go
│   │   ├── spy.go
│   │   └── spy_test.go
│   ├── fxreflect/
│   │   ├── fxreflect.go
│   │   ├── fxreflect_test.go
│   │   ├── stack.go
│   │   └── stack_test.go
│   ├── leaky_test/
│   │   └── leaky_test.go
│   ├── lifecycle/
│   │   ├── lifecycle.go
│   │   └── lifecycle_test.go
│   └── testutil/
│       ├── writer.go
│       └── writer_test.go
├── invoke.go
├── lifecycle.go
├── log.go
├── log_test.go
├── module.go
├── module_test.go
├── populate.go
├── populate_example_test.go
├── populate_test.go
├── printer_writer.go
├── provide.go
├── replace.go
├── replace_test.go
├── shutdown.go
├── shutdown_test.go
├── signal.go
├── signal_test.go
├── supply.go
├── supply_test.go
├── tools/
│   ├── analysis/
│   │   └── passes/
│   │       └── allfxevents/
│   │           ├── analysis.go
│   │           ├── analysis_test.go
│   │           └── testdata/
│   │               └── src/
│   │                   ├── a/
│   │                   │   ├── full.go
│   │                   │   ├── nop.go
│   │                   │   ├── not_a_logger.go
│   │                   │   ├── partial_test.go
│   │                   │   ├── ptr.go
│   │                   │   └── value.go
│   │                   ├── b/
│   │                   │   ├── fxevent/
│   │                   │   │   └── logger.go
│   │                   │   └── not_real_fxevent.go
│   │                   └── go.uber.org/
│   │                       └── fx/
│   │                           └── fxevent/
│   │                               ├── fxevent.go
│   │                               └── partial.go
│   ├── cmd/
│   │   └── fxlint/
│   │       └── main.go
│   ├── go.mod
│   └── go.sum
└── version.go

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

================================================
FILE: .codecov.yml
================================================
ignore:
  - "docs/ex/**/*.go"
  - "internal/e2e/**/*.go"

coverage:
  range: 80..100
  round: down
  precision: 2

  status:
    project:                   # measuring the overall project coverage
      default:                 # context, you can create multiple ones with custom titles
        enabled: yes           # must be yes|true to enable this status
        target: 90%             # specify the target coverage for each commit status
                               #   option: "auto" (must increase from parent commit or pull request base)
                               #   option: "X%" a static target percentage to hit
        if_not_found: success  # if parent is not found report status as success, error, or failure
        if_ci_failed: error    # if ci fails report status as success, error, or failure


================================================
FILE: .github/ISSUE_TEMPLATE/bug_report.md
================================================
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: ''
assignees: ''

---

**Describe the bug**
A clear and concise description of what the bug is.

**To Reproduce**
Steps to reproduce the behavior

**Expected behavior**
A clear and concise description of what you expected to happen.

**Additional context**
Add any other context about the problem here.


================================================
FILE: .github/ISSUE_TEMPLATE/config.yml
================================================
contact_links:
  - name: Questions
    about: Please use our Discussions page
    url: https://github.com/uber-go/fx/discussions


================================================
FILE: .github/ISSUE_TEMPLATE/feature_request.md
================================================
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: ''
assignees: ''

---

**Is your feature request related to a problem? Please describe.**

**Describe the solution you'd like**
A clear and concise description of what you want to happen.

**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.

**Is this a breaking change?**
We 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.

**Additional context**
Add any other context or screenshots about the feature request here.


================================================
FILE: .github/dependabot.yml
================================================
version: 2
updates:
  - package-ecosystem: "github-actions"
    directory: "/"
    schedule:
      interval: "weekly"

  # Auto-update tools dependencies, but not library dependencies.
  - package-ecosystem: "gomod"
    directory: "/tools"
    schedule:
      interval: "weekly"


================================================
FILE: .github/workflows/docs.yml
================================================
name: GitHub Pages

on:
  push:
    tags: ['v*']

  # This lets us publish the workflow
  # manually from the GitHub Actions UI.
  #
  # It expects a single input:
  # the Git ref we want to build and publish the docs for.
  workflow_dispatch:
    inputs:
      head:
        description: "Git commitish to check out."
        required: true
        type: string


# Run at most one publish job at a time,
# cancelling others if a new one starts.
concurrency:
  group: "pages"
  cancel-in-progress: true


env:
  UV_VERSION: 0.3.3

jobs:
  build:
    runs-on: ubuntu-latest

    steps:
      # We have two checkout steps running based on
      # whether the workflow was manually triggered.
      # If manually triggered, we use the provided ref,
      # otherwise we use the default ref.
      - name: Checkout (on release)
        if:  github.event_name != 'workflow_dispatch'
        uses: actions/checkout@v4
      - name: Checkout (manual)
        if:  github.event_name == 'workflow_dispatch'
        uses: actions/checkout@v4
        with:
          ref: ${{ inputs.head }}

      - name: Install uv
        run: |
          curl -LsSf "https://astral.sh/uv/${UV_VERSION}/install.sh" | sh

      - name: Build
        run: make docs

      - name: Upload artifact
        uses: actions/upload-pages-artifact@v3
        with:
          path: docs/_site

  deploy:
    needs: build  # run only after a successful build

    permissions:
      pages: write      # to deploy to Pages
      id-token: write   # to verify the deployment originates from an appropriate source

    environment:
      name: github-pages
      url: ${{ steps.deployment.outputs.page_url }}

    runs-on: ubuntu-latest
    steps:
      - name: Deploy to GitHub Pages
        id: deployment
        uses: actions/deploy-pages@v4


================================================
FILE: .github/workflows/fossa.yaml
================================================
name: FOSSA Analysis
on: push

jobs:

  build:
    runs-on: ubuntu-latest
    if: github.repository_owner == 'uber-go'
    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: FOSSA analysis
        uses: fossas/fossa-action@v1
        with:
          api-key: ${{ secrets.FOSSA_API_KEY }}

permissions:
  contents: read


================================================
FILE: .github/workflows/go.yml
================================================
name: Go

on:
  push:
    branches: ['*']
    tags: ['v*']
  pull_request:
    branches: ['*']

permissions:
  contents: read

jobs:

  build:
    runs-on: ${{ matrix.os }}
    name: Build and test

    strategy:
      matrix:
        os: ["ubuntu-latest", "windows-latest"]
        go: ["1.24.x", "1.25.x"]

    steps:
    - name: Checkout code
      uses: actions/checkout@v4

    - name: Setup Go
      uses: actions/setup-go@v5
      with:
        go-version: ${{ matrix.go }}

    - name: Download Dependencies
      run: go mod download

    - name: Test
      run: make cover

    - name: Upload coverage to codecov.io
      uses: codecov/codecov-action@v4
      env:
        CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}

  doctest:
    runs-on: ubuntu-latest
    name: Test documentation

    steps:
    - name: Checkout code
      uses: actions/checkout@v4

    - name: Setup Go
      uses: actions/setup-go@v5
      with:
        go-version: 1.25.x

    - name: Test
      run: make cover COVER_MODULES=./docs

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

    steps:
    - name: Checkout code
      uses: actions/checkout@v4

    - name: Setup Go
      uses: actions/setup-go@v5
      with:
        go-version: 1.25.x

    - uses: golangci/golangci-lint-action@v8
      name: Install golangci-lint
      with:
        version: latest
        args: --help  # make lint will run the linter

    - run: make lint
      name: Lint


================================================
FILE: .gitignore
================================================
/vendor
/.bench
*.mem
*.cpu
*.test
*.log
*.out
*.html
*.coverprofile
coverage.txt
*.pprof
/.bin
/.cache
/bin
.vscode
.mdoxcache


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

issues:
  # Print all issues reported by all linters.
  max-issues-per-linter: 0
  max-same-issues: 0

linters:
  # We'll track the golangci-lint default linters manually
  # instead of letting them change without our control.
  default: none
  enable:
    # golangci-lint defaults:
    - govet
    - ineffassign
    - staticcheck
    - unused

    # Our own extras:
    - errorlint
    - nolintlint  # lints //nolint directives
    - revive

    # License header check:
    - goheader

  settings:

    # These govet checks are disabled by default, but they're useful.
    govet:
      enable:
        - nilness
        - reflectvaluecompare
        - sortslice
        - unusedwrite

    goheader:
      values:
        const:
          COMPANY: Uber Technologies, Inc.
        regexp:
          YEAR_RANGE: \d{4}(-\d{4})?
      template: |-
        Copyright (c) {{ YEAR_RANGE }} {{ COMPANY }}

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

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

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

  exclusions:
    generated: lax
    rules:
      # Don't warn on unused parameters.
      # Parameter names are useful; replacing them with '_' is undesirable.
      - linters: [revive]
        text: 'unused-parameter: parameter \S+ seems to be unused, consider removing or renaming it as _'

      # staticcheck already has smarter checks for empty blocks.
      # revive's empty-block linter has false positives.
      # For example, as of writing this, the following is not allowed.
      #   for foo() { }
      - linters: [revive]
        text: 'empty-block: this block is empty, you can remove it'

      # It's okay if internal packages and examples in docs/
      # don't have package comments.
      - linters: [revive]
        path: .+/internal/.+|^internal/.+|^docs/.+
        text: should have a package comment

      # It's okay for tests to use dot imports.
      - linters: [revive]
        path: _test\.go$
        text: should not use dot imports

formatters:
  enable: [gofumpt]
  exclusions:
    generated: lax


================================================
FILE: CHANGELOG.md
================================================
---
search:
  exclude: true
---

# Changelog

All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## Unreleased
- No changes yet.

## [1.24.0](https://github.com/uber-go/fx/compare/v1.23.0...v1.24.0) - 2025-05-13

### Added
- A new event `fxevent.BeforeRun` is now emitted before Fx runs a constructor, 
  decorator, or supply/replace stub.

### Changed
- Clearer error messages are now used when annotation building fails.

## [1.23.0](https://github.com/uber-go/fx/compare/v1.22.2...v1.22.3) - 2024-10-11

### Added
- Added `Runtime` to `fxevent.Run` event, which stores the runtime of
  a constructor or a decorator that's run, including functions created
  by `fx.Supply` and `fx.Replace`.

### Changed
- Overhauled the documentation website. (https://uber-go.github.io/fx/)

## [1.22.2](https://github.com/uber-go/fx/compare/v1.22.1...v1.22.2) - 2024-08-07

### Fixed
- A deadlock with the relayer in signal receivers.

### Changed
- Upgrade Dig dependency to v1.18.0

## [1.22.1](https://github.com/uber-go/fx/compare/v1.22.0...v1.22.1) - 2024-06-25

### Fixed
- Fx apps will only listen to signals when `.Run()`, `.Wait()`, or `.Done()`
  are called, fixing a regression introduced in v1.19.0.

## [1.22.0](https://github.com/uber-go/fx/compare/v1.21.1...v1.22.0) - 2024-05-30

### Added
- Add `fx.Self` which can be passed to the `fx.As` annotation to signify
  that a type should be provided as itself.
- Add `fxtest.EnforceTimeout` that can be passed to `fxtest.NewLifecycle`
  to force `Start` and `Stop` to return context errors when hook context expires.

### Changed
- `fx.Private` can now be used with `fx.Supply`.

### Fixed
- Fx apps will no longer listen to OS signals when they are stopped,
  solving blocking issues in programs that depended on OS signals
  after an Fx app stops.

## [1.21.1](https://github.com/uber-go/fx/compare/v1.21.0...v1.21.1) - 2024-04-24

### Changed
- Register Fx provides (e.g. fx.Lifecycle, fx.Shutdowner, fx.DotGraph) before
  user provides, to increase likelihood of successful custom logger creation.

## [1.21.0](https://github.com/uber-go/fx/compare/v1.20.1...v1.21.0) - 2024-03-13

### Added
- fxtest: Add WithTestLogger option that uses a `testing.TB` as the
  Fx event logger.
- An fxevent logger that can log events using a slog logger has been added.

### Changed
- Upgrade Dig dependency to v1.17.1

## [1.20.1](https://github.com/uber-go/fx/compare/v1.20.0...v1.20.1) - 2023-10-17

### Added
- Provided, Decorated, Supplied, and Replaced events now include a trace
  of module locations through which the option was given to the App.
- wasi support.

## [1.20.0](https://github.com/uber-go/fx/compare/v1.19.3...v1.20.0) - 2023-06-12

### Added
- A new event `fxevent.Run` is now emitted when Fx runs a constructor, decorator,
  or supply/replace stub.

### Changed
- `fx.Populate` now works with `fx.Annotate`.
- Upgrade Dig dependency to v1.17.0.

## [1.19.3](https://github.com/uber-go/fx/compare/v1.19.2...v1.19.3) - 2023-04-17

### Changed
- Fixed several typos in docs.
- WASM build support.
- Annotating In and Out structs with From/As annotations generated invalid results.
  The annotation check now blocks this.
- `Shutdown`: Support calling from `Invoke`.

### Deprecated
- Deprecate `ShutdownTimeout` option.

### Fixed
- Respect Shutdowner ExitCode from calling `Run`.

## [1.19.2](https://github.com/uber-go/fx/compare/v1.19.1...v1.19.2) - 2023-02-21

### Changed
- Update Dig dependency to v1.16.1.

## [1.19.1](https://github.com/uber-go/fx/compare/v1.19.0...v1.19.1) - 2023-01-10

### Changed
- Calling `fx.Stop()` after the `App` has already stopped no longer errors out.

### Fixed
- Addressed a regression in 1.19.0 release which caused apps to ignore OS signals
  after running for startTimeout duration.

## [1.19.0](https://github.com/uber-go/fx/compare/v1.18.2...v1.19.0) - 2023-01-03

### Added
- `fx.RecoverFromPanics` Option which allows Fx to recover from user-provided constructors
  and invoked functions.
- `fx.Private` that allows the constructor to limit the scope of its outputs to the wrapping
  `fx.Module`.
- `ExitCode` ShutdownOption which allows setting custom exit code at the end of app
  lifecycle.
- `Wait` which returns a channel that can be used for waiting on application shutdown.
- fxevent/ZapLogger now exposes `UseLogLevel` and `UseErrorLevel` methods to set
  the level of the Zap logs produced by it.
- Add lifecycle hook-convertible methods: `StartHook`, `StopHook`, `StartStopHook`
  that can be used with more function signatures.

### Changed
- `fx.WithLogger` can now be passed at `fx.Module` level, setting custom logger at
  `Module` scope instead of the whole `App`.

### Fixed
- `fx.OnStart` and `fx.OnStop` Annotations now work with annotated types that was
  provided by the annotated constructor.
- fxevent/ZapLogger: Errors from `fx.Supply` are now logged at `Error` level, not
  `Info`.
- A race condition in lifecycle Start/Stop methods.
- Typos in docs.

## [1.18.2](https://github.com/uber-go/fx/compare/v1.18.1...v1.18.2) - 2022-09-28

### Added
- Clarify ordering of `Invoke`s in `Module`s.

### Fixed
- Fix `Decorate` not being applied to transitive dependencies at root `App` level.

## [1.18.1](https://github.com/uber-go/fx/compare/v1.18.0...v1.18.1) - 2022-08-08

### Fixed
- Fix a nil panic when `nil` is passed to `OnStart` and `OnStop` lifecycle methods.

## [1.18.0](https://github.com/uber-go/fx/compare/v1.17.1...v1.18.0) - 2022-08-05

### Added
- Soft value groups that lets you specify value groups as best-effort dependencies.
- `fx.OnStart` and `fx.OnStop` annotations which lets you annotate dependencies to provide
  OnStart and OnStop lifecycle hooks.
- A new `fxevent.Replaced` event written to `fxevent.Logger` following an `fx.Replace`.

### Fixed
- Upgrade Dig dependency to v1.14.1 to address a couple of issues with decorations. Refer to
  Dig v1.14.1 release notes for more details.
- `fx.WithLogger` no longer ignores decorations and replacements of types that
  it depends on.
- Don't run lifecycle hooks if the context for them has already expired.
- `App.Start` and `App.Stop` no longer deadlock if the OnStart/OnStop hook
  exits the current goroutine.
- `fxevent.ConsoleLogger` no longer emits an extraneous argument for the
  Supplied event.

### Deprecated
- `fx.Extract` in favor of `fx.Populate`.

## [1.17.1](https://github.com/uber-go/fx/compare/v1.17.0...v1.17.1) - 2022-03-23

### Added
- Logging for provide/invoke/decorate now includes the associated `fx.Module` name.

## [1.17.0](https://github.com/uber-go/fx/compare/v1.16.0...v1.17.0) - 2022-02-28

### Added
- Add `fx.Module` which scopes any modifications made to the dependency graph.
- Add `fx.Decorate` and `fx.Replace` that lets you modify a dependency graph with decorators.
- Add `fxevent.Decorated` event which gets emitted upon a dependency getting decorated.

### Changed
- `fx.Annotate`: Validate that `fx.In` or `fx.Out` structs are not passed to it.
- `fx.Annotate`: Upon failure to Provide, the error contains the actual location
  of the provided constructor.

## [1.16.0](https://github.com/uber-go/fx/compare/v1.15.0...v1.16.0) - 2021-12-02

### Added
- Add the ability to provide a function as multiple interfaces at once using `fx.As`.

### Changed
- `fx.Annotate`: support variadic functions, and feeding value groups into them.

### Fixed
- Fix an issue where OnStop hooks weren't getting called on SIGINT on Windows.
- Fix a data race between app.Done() and shutdown.

## [1.15.0](https://github.com/uber-go/fx/compare/v1.14.2...v1.15.0) - 2021-11-08

### Added
- Add `fx.Annotate` to allow users to provide parameter and result tags easily without
  having to create `fx.In` or `fx.Out` structs.
- Add `fx.As` that allows users to annotate a constructor to provide its result type(s) as
  interface(s) that they implement instead of the types themselves.

### Fixed
- Fix `fxevent.Stopped` not being logged when `App.Stop` is called.
- Fix `fxevent.Started` or `fxevent.Stopped` not being logged when start or
  stop times out.

## [1.14.2](https://github.com/uber-go/fx/compare/v1.14.1...v1.14.2) - 2021-08-16

### Changed
- For `fxevent` console implementation: no longer log non-error case for `fxevent.Invoke`
  event, while for zap implementation, start logging `fx.Invoking` case without stack.

## [1.14.1](https://github.com/uber-go/fx/compare/v1.14.0...v1.14.1) - 2021-08-16

### Changed
- `fxevent.Invoked` was being logged at `Error` level even upon successful `Invoke`.
  This was changed to log at `Info` level when `Invoke` succeeded.

## [1.14.0](https://github.com/uber-go/fx/compare/v1.13.1...v1.14.0) - 2021-08-12

### Added
- Introduce the new `fx.WithLogger` option. Provide a constructor for
  `fxevent.Logger` objects with it to customize how Fx logs events.
- Add new `fxevent` package that exposes events from Fx in a structured way.
  Use this to write custom logger implementations for use with the
  `fx.WithLogger` option.
- Expose and log additional information when lifecycle hooks time out.

### Changed
- Fx now emits structured JSON logs by default. These may be parsed and
  processed by log ingestion systems.
- `fxtest.Lifecycle` now logs to the provided `testing.TB` instead of stderr.
- `fx.In` and `fx.Out` are now type aliases instead of structs.

## [1.13.1](https://github.com/uber-go/fx/compare/v1.13.0...v1.13.1) - 2020-08-19

### Fixed
- Fix minimum version constraint for dig. `fx.ValidateGraph` requires at least
  dig 1.10.

## [1.13.0](https://github.com/uber-go/fx/compare/v1.12.0...v1.13.0) - 2020-06-16

### Added
- Added `fx.ValidateGraph` which allows graph cycle validation and dependency correctness
  without running anything. This is useful if `fx.Invoke` has side effects, does I/O, etc.

## [1.12.0](https://github.com/uber-go/fx/compare/v1.11.0...v1.12.0) - 2020-04-09

### Added
- Added `fx.Supply` to provide externally created values to Fx containers
  without building anonymous constructors.

### Changed
- Drop library dependency on development tools.

## [1.11.0](https://github.com/uber-go/fx/compare/v1.10.0...v1.11.0) - 2020-04-01

### Added
- Value groups can use the `flatten` option to indicate values in a slice should
  be provided individually rather than providing the slice itself. See package
  documentation for details.

## [1.10.0](https://github.com/uber-go/fx/compare/v1.9.0...v1.10.0) - 2019-11-20

### Added
- All `fx.Option`s now include readable string representations.
- Report stack traces when `fx.Provide` and `fx.Invoke` calls fail. This
  should make these errors more debuggable.

### Changed
- Migrated to Go modules.

## [1.9.0](https://github.com/uber-go/fx/compare/v1.8.0...v1.9.0) - 2019-01-22

### Added
- Add the ability to shutdown Fx applications from inside the container. See
  the Shutdowner documentation for details.
- Add `fx.Annotated` to allow users to provide named values without creating a
  new constructor.

## [1.8.0](https://github.com/uber-go/fx/compare/v1.7.1...v1.8.0) - 2018-11-06

### Added
- Provide DOT graph of dependencies in the container.

## [1.7.1](https://github.com/uber-go/fx/compare/v1.7.0...v1.7.1) - 2018-09-26

### Fixed
- Make `fxtest.New` ensure that the app was created successfully. Previously,
  it would return the app (similar to `fx.New`, which expects the user to verify
  the error).
- Update dig container to defer acyclic validation until after Invoke. Application
  startup time should improve proportional to the size of the dependency graph.
- Fix a goroutine leak in `fxtest.Lifecycle`.

## [1.7.0](https://github.com/uber-go/fx/compare/v1.6.0...v1.7.0) - 2018-08-16

### Added
- Add `fx.ErrorHook` option to allow users to provide `ErrorHandler`s on invoke
  failures.
- `VisualizeError` returns the visualization wrapped in the error if available.

## [1.6.0](https://github.com/uber-go/fx/compare/v1.5.0...v1.6.0) - 2018-06-12

### Added
- Add `fx.Error` option to short-circuit application startup.

## [1.5.0](https://github.com/uber-go/fx/compare/v1.4.0...v1.5.0) - 2018-04-11

### Added
- Add `fx.StartTimeout` and `fx.StopTimeout` to make configuring application
  start and stop timeouts easier.
- Export the default start and stop timeout as `fx.DefaultTimeout`.

### Fixed
- Make `fxtest` respect the application's start and stop timeouts.

## [1.4.0](https://github.com/uber-go/fx/compare/v1.3.0...v1.4.0) - 2017-12-07

### Added
- Add `fx.Populate` to populate variables with values from the dependency
  injection container without requiring intermediate structs.

## [1.3.0](https://github.com/uber-go/fx/compare/v1.2.0...v1.3.0) - 2017-11-28

### Changed
- Improve readability of hook logging in addition to provide and invoke.

### Fixed
- Fix bug which caused the OnStop for a lifecycle hook to be called even if it
  failed to start.

## [1.2.0](https://github.com/uber-go/fx/compare/v1.1.0...v1.2.0) - 2017-09-06

### Added
- Add `fx.NopLogger` which disables the Fx application's log output.

## [1.1.0](https://github.com/uber-go/fx/compare/v1.0.0...v1.1.0) - 2017-08-22

### Changed
- Improve readability of start up logging.

## [1.0.0](https://github.com/uber-go/fx/compare/v1.0.0-rc2...v1.0.0) - 2017-07-31

First stable release: no breaking changes will be made in the 1.x series.

### Added
- `fx.Extract` now supports `fx.In` tags on target structs.

### Changed
- **[Breaking]** Rename `fx.Inject` to `fx.Extract`.
- **[Breaking]** Rename `fxtest.Must*` to `fxtest.Require*`.

### Removed
- **[Breaking]** Remove `fx.Timeout` and `fx.DefaultTimeout`.

## [1.0.0-rc2](https://github.com/uber-go/fx/compare/v1.0.0-rc1...v1.0.0-rc2) - 2017-07-21

- **[Breaking]** Lifecycle hooks now take a context.
- Add `fx.In` and `fx.Out` which exposes optional and named types.
  Modules should embed these types instead of relying on `dig.In` and `dig.Out`.
- Add an `Err` method to retrieve the underlying errors during the dependency
  graph construction. The same error is also returned from `Start`.
- Graph resolution now happens as part of `fx.New`, rather than at the beginning
  of `app.Start`. This allows inspection of the graph errors through `app.Err()`
  before the decision to start the app.
- Add a `Logger` option, which allows users to send Fx's logs to different
  sink.
- Add `fxtest.App`, which redirects log output to the user's `testing.TB` and
  provides some lifecycle helpers.

## [1.0.0-rc1](https://github.com/uber-go/fx/compare/v1.0.0-beta4...v1.0.0-rc1) - 2017-06-20

- **[Breaking]** Providing types into `fx.App` and invoking functions are now
  options passed during application construction. This makes users'
  interactions with modules and collections of modules identical.
- **[Breaking]** `TestLifecycle` is now in a separate `fxtest` subpackage.
- Add `fx.Inject()` to pull values from the container into a struct.

## [1.0.0-beta4](https://github.com/uber-go/fx/compare/v1.0.0-beta3...v1.0.0-beta4) - 2017-06-12

- **[Breaking]** Monolithic framework, as released in initial betas, has been
  broken into smaller pieces as a result of recent advances in `dig` library.
  This is a radical departure from the previous direction, but it needed to
  be done for the long-term good of the project.
- **[Breaking]** `Module interface` has been scoped all the way down to being
  *a single dig constructor*. This allows for very sophisticated module
  compositions. See `go.uber.org/dig` for more information on the constructors.
- **[Breaking]** `package config` has been moved to its own repository.
  see `go.uber.org/config` for more information.
- `fx.Lifecycle` has been added for modules to hook into the framework
  lifecycle events.
- `service.Host` interface which composed a number of primitives together
  (configuration, metrics, tracing) has been deprecated in favor of
  `fx.App`.

## [1.0.0-beta3](https://github.com/uber-go/fx/compare/v1.0.0-beta2...v1.0.0-beta3) - 2017-03-28

- **[Breaking]** Environment config provider was removed. If you were using
  environment variables to override YAML values, see config documentation for
  more information.
- **[Breaking]** Simplify Provider interface: remove `Scope` method from the
  `config.Provider` interface, one can use either ScopedProvider and Value.Get()
  to access sub fields.
- Add `task.MustRegister` convenience function which fails fast by panicking
  Note that this should only be used during app initialization, and is provided
  to avoid repetetive error checking for services which register many tasks.
- Expose options on task module to disable execution. This will allow users to
  enqueue and consume tasks on different clusters.
- **[Breaking]** Rename Backend interface `Publish` to `Enqueue`. Created a new
  `ExecuteAsync` method that will kick off workers to consume tasks and this is
  subsumed by module Start.
- **[Breaking]** Rename package `uhttp/client` to `uhttp/uhttpclient` for clarity.
- **[Breaking]** Rename `PopulateStruct` method in value to `Populate`.
  The method can now populate not only structs, but anything: slices,
  maps, builtin types and maps.
- **[Breaking]** `package dig` has moved from `go.uber.org/fx/dig` to a new home
  at `go.uber.org/dig`.
- **[Breaking]** Pass a tracer the `uhttp/uhttpclient` constructor explicitly, instead
  of using a global tracer. This will allow to use http client in parallel tests.

## [1.0.0-beta2](https://github.com/uber-go/fx/compare/v1.0.0-beta1...v1.0.0-beta2) - 2017-03-09

- **[Breaking]** Remove `ulog.Logger` interface and expose `*zap.Logger` directly.
- **[Breaking]** Rename config and module from `modules.rpc` to `modules.yarpc`
- **[Breaking]** Rename config key from `modules.http` to `modules.uhttp` to match
  the module name
- **[Breaking]** Upgrade `zap` to `v1.0.0-rc.3` (now go.uber.org/zap, was
  github.com/uber-go/zap)
- Remove now-unused `config.IsDevelopmentEnv()` helper to encourage better
  testing practices. Not a breaking change as nobody is using this func
  themselves according to our code search tool.
- Log `traceID` and `spanID` in hex format to match Jaeger UI. Upgrade Jaeger to
  min version 2.1.0
  and use jaeger's adapters for jaeger and tally initialization.
- Tally now supports reporting histogram samples for a bucket. Upgrade Tally to 2.1.0
- **[Breaking]** Make new module naming consistent `yarpc.ThriftModule` to
  `yarpc.New`, `task.NewModule`
  to `task.New`
- **[Breaking]** Rename `yarpc.CreateThriftServiceFunc` to `yarpc.ServiceCreateFunc`
  as it is not thrift-specific.
- Report version metrics for company-wide version usage information.
- Allow configurable service name and module name via service options.
- DIG constructors now support returning a tuple with the second argument being
  an error.

## 1.0.0-beta1 - 2017-02-20

This is the first beta release of the framework, where we invite users to start
building services on it and provide us feedback. **Warning** we are not
promising API compatibility between beta releases and the final 1.0.0 release.
In fact, we expect our beta user feedback to require some changes to the way
things work. Once we reach 1.0, we will provider proper version compatibility.


================================================
FILE: CONTRIBUTING.md
================================================
---
search:
  exclude: true
---

# Contributing

Thanks for helping to make Fx better for everyone!

If you'd like to add new exported APIs,
please [open an issue](https://github.com/uber-go/fx/issues/new)
describing your proposal.
Discussing API changes ahead of time makes pull request review much smoother.

!!! tip

    You'll need to sign [Uber's CLA](https://cla-assistant.io/uber-go/fx)
    before we can accept any of your contributions.
    If necessary, a bot will remind
    you to accept the CLA when you open your pull request.


## Contribute code

Set up your local development environment to contribute to Fx.

1. [Fork](https://github.com/uber-go/fx/fork), then clone the repository.

    === "Git"

        ```bash
        git clone https://github.com/your_github_username/fx.git
        cd fx
        git remote add upstream https://github.com/uber-go/fx.git
        git fetch upstream
        ```

    === "GitHub CLI"

        ```bash
        gh repo fork --clone uber-go/fx
        ```

2. Install Fx's dependencies:

     ```bash
     go mod download
     ```

3. Verify that tests and other checks pass locally.

     ```bash
     make lint
     make test
     ```

     Note that for `make lint` to work,
     you must be using the latest stable version of Go.
     If you're on an older version, you can still contribute your change,
     but we may discover style violations when you open the pull request.

Next, make your changes.

1. Create a new feature branch.

     ```bash
     git checkout master
     git pull
     git checkout -b cool_new_feature
     ```

2. Make your changes, and verify that all tests and lints still pass.

     ```bash
     $EDITOR app.go
     make lint
     make test
     ```

3. When you're satisfied with the change,
   push it to your fork and make a pull request.

    === "Git"

        ```bash
        git push origin cool_new_feature
        # Open a PR at https://github.com/uber-go/fx/compare
        ```

    === "GitHub CLI"

        ```bash
        gh pr create
        ```

At this point, you're waiting on us to review your changes.
We *try* to respond to issues and pull requests within a few business days,
and we may suggest some improvements or alternatives.
Once your changes are approved, one of the project maintainers will merge them.

The review process will go more smoothly if you:

- add tests for new functionality
- write a [good commit message](https://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html)
- maintain backward compatibility
- follow our [style guide](https://github.com/uber-go/guide/blob/master/style.md)

## Contribute documentation

To contribute documentation to Fx,

1. Set up your local development environment
   as you would to [contribute code](#contribute-code).

2. [Install uv](https://docs.astral.sh/uv/getting-started/installation/).
   We use this to manage our Python dependencies.

3. Run the development server.

     ```bash
     make serve
     ```

4. Make your changes.

Documentation changes should adhere to the guidance laid out below.

### Document by purpose

Documentation is organized in one of the following categories.

- **Tutorials**: These hold step-by-step instructions for an end-to-end project
  that a beginner could follow along to.
  Don't spend time explaining things.
  If explanations are available elsewhere, link to them.
  These are entry points to answer the prompt,
  "I don't know what Fx is, show me what it can do,"
  so there won't be too many of these.
- **Explanations**: These hold long-form explanations of concepts and ideas.
  These are intended to build an understanding of Fx.
  Feel free to go wild here--use learning aids like diagrams, tables, etc.
- **How-tos**: These are step-by-step instructions for a *specific problem*.
  Unlike tutorials, these are not meant to be end-to-end.
  Feel free to leave things out, make assumptions,
  or provide options ("if you're doing this, do this").
  As with tutorials, don't spend time explaining;
  link to explanations elsewhere.

As an example,

- A tutorial will use lifecycle hooks as part of
  a larger set of instructions for a full end-to-end application.
- An explanation will explain what lifecycle hooks are, how they work,
  when and how you should use them, and link to relevant APIs and guides.
- A how-to guide will demonstrate how to use lifecycle hooks
  with an HTTP server, a gRPC server, etc.

Explanations and how-to guides are often on the same page,
but they should be in distinct sections.

This separation is inspired by the
[Divio documentation system](https://documentation.divio.com/),

### Formatting

#### ATX-style headers

Use ATX-style headers (`#`-prefixed),
not Setext-style (underlined with `===` or `---`).

```markdown
Bad header
==========

## Good header
```

#### Semantic Line Breaks

- **Do not** write overly long lines of text
- **Do not** "reflow" Markdown paragraphs
- **Do** use [Semantic Line Breaks](https://sembr.org/) to break these lines down

```markdown
This 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.

This is a bad paragraph because even though it's not all one one line, it adds
line breaks when it reaches the line length limit. This means that anytime I
change anything in this paragraph, I have to "reflow" it, which will change
other lines and make the change I'm making more difficult to review.

This is a good paragraph. It uses semantic line breaks.
I can add words or modify an existing sentence,
or even parts of a sentence,
easily and without affecting other lines.
When I change something, the actual change I made is easy to review.
Markdown will reflow this into a "normal" pargraph when rendering.
```

### Test everything

All code samples in documentation must be buildable and testable.

To make this possible, we put code samples in the "ex/" directory,
and use the [PyMdown Snippets extension](https://facelessuser.github.io/pymdown-extensions/extensions/snippets/)
to include them in the documentation.

To include code snippets in your documentation,
take the following steps:

1. Add source code under the `ex/` directory.
   Usually, the file will be placed in a directory
   with a name matching the documentation file
   that will include the snippet.

     For example,
     for src/annotation.md, examples will reside in ex/annotation/.

2. Inside the source file, name regions of code with comments in the forms:

     ```
     // --8<-- [start:name]
     ...
     // --8<-- [end:name]
     ```

     Where `name` is the name of the snippet.
     For example:

     ```go
     // --8<-- [start:New]
     func New() *http.Server {
         // ...
     }
     // --8<-- [end:New]
     ```

3. Include the snippet in a code block with the following syntax:

    ```markdown
    ```go
    ;--8<-- "path/to/file.go:name"
    ```

    Where `path/to/file.go` is the path to the file containing the snippet
    relative to the `ex/` directory,
    and `name` is the name of the snippet.

    You can include multiple snippets from the same file like so:

    ```
    ;--8<-- "path/to/file.go:snippet1"
    ;--8<-- "path/to/file.go:snippet2"
    ```


================================================
FILE: LICENSE
================================================
Copyright (c) 2016-2018 Uber Technologies, Inc.

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

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

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


================================================
FILE: Makefile
================================================
# Directory containing the Makefile.
PROJECT_ROOT = $(dir $(abspath $(lastword $(MAKEFILE_LIST))))

export GOBIN ?= $(PROJECT_ROOT)/bin
export PATH := $(GOBIN):$(PATH)

FXLINT = $(GOBIN)/fxlint

MODULES = . ./tools ./docs ./internal/e2e

# 'make cover' should not run on docs by default.
# We run that separately explicitly on a specific platform.
COVER_MODULES ?= $(filter-out ./docs,$(MODULES))

.PHONY: all
all: build lint test

.PHONY: build
build:
	go build ./...

.PHONY: lint
lint: golangci-lint tidy-lint fx-lint

.PHONY: test
test:
	@$(foreach dir,$(MODULES),(cd $(dir) && go test -race ./...) &&) true

.PHONY: cover
cover:
	@$(foreach dir,$(COVER_MODULES), \
		(cd $(dir) && \
		echo "[cover] $(dir)" && \
		go test -race -coverprofile=cover.out -coverpkg=./... ./... && \
		go tool cover -html=cover.out -o cover.html) &&) true

.PHONY: tidy
tidy:
	@$(foreach dir,$(MODULES),(cd $(dir) && go mod tidy) &&) true

.PHONY: docs
docs:
	cd docs && make build

.PHONY: golangci-lint
golangci-lint:
	@$(foreach mod,$(MODULES), \
		(cd $(mod) && \
		echo "[lint] golangci-lint: $(mod)" && \
		golangci-lint run --path-prefix $(mod)) &&) true

.PHONY: tidy-lint
tidy-lint:
	@$(foreach mod,$(MODULES), \
		(cd $(mod) && \
		echo "[lint] tidy: $(mod)" && \
		go mod tidy -diff) &&) true

.PHONY: fx-lint
fx-lint: $(FXLINT)
	@$(FXLINT) ./...

$(FXLINT): tools/cmd/fxlint/main.go
	cd tools && go install go.uber.org/fx/tools/cmd/fxlint


================================================
FILE: README.md
================================================
# :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)

Fx is a dependency injection system for Go.

**Benefits**

- Eliminate globals: Fx helps you remove global state from your application.
  No more `init()` or global variables. Use Fx-managed singletons.
- Code reuse: Fx lets teams within your organization build loosely-coupled
  and well-integrated shareable components.
- Battle tested: Fx is the backbone of nearly all Go services at Uber.

See our [docs](https://uber-go.github.io/fx/) to get started and/or
learn more about Fx.

## Installation

Use Go modules to install Fx in your application.

```shell
go get go.uber.org/fx@v1
```

## Getting started

To get started with Fx, [start here](https://uber-go.github.io/fx/get-started/).

## Stability

This library is `v1` and follows [SemVer](https://semver.org/) strictly.

No breaking changes will be made to exported APIs before `v2.0.0`.

This project follows the [Go Release Policy](https://golang.org/doc/devel/release.html#policy). Each major
version of Go is supported until there are two newer major releases.

## Stargazers over time

[![Stargazers over time](https://starchart.cc/uber-go/fx.svg)](https://starchart.cc/uber-go/fx)



================================================
FILE: annotated.go
================================================
// Copyright (c) 2020-2021 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

package fx

import (
	"context"
	"errors"
	"fmt"
	"reflect"
	"strings"

	"go.uber.org/dig"
	"go.uber.org/fx/internal/fxreflect"
)

// Annotated annotates a constructor provided to Fx with additional options.
//
// For example,
//
//	func NewReadOnlyConnection(...) (*Connection, error)
//
//	fx.Provide(fx.Annotated{
//	  Name: "ro",
//	  Target: NewReadOnlyConnection,
//	})
//
// Is equivalent to,
//
//	type result struct {
//	  fx.Out
//
//	  Connection *Connection `name:"ro"`
//	}
//
//	fx.Provide(func(...) (result, error) {
//	  conn, err := NewReadOnlyConnection(...)
//	  return result{Connection: conn}, err
//	})
//
// Annotated cannot be used with constructors which produce fx.Out objects.
// When used with [Supply], Target is a value instead of a constructor.
//
// This type represents a less powerful version of the [Annotate] construct;
// prefer [Annotate] where possible.
type Annotated struct {
	// If specified, this will be used as the name for all non-error values returned
	// by the constructor. For more information on named values, see the documentation
	// for the fx.Out type.
	//
	// A name option may not be provided if a group option is provided.
	Name string

	// If specified, this will be used as the group name for all non-error values returned
	// by the constructor. For more information on value groups, see the package documentation.
	//
	// A group option may not be provided if a name option is provided.
	//
	// Similar to group tags, the group name may be followed by a `,flatten`
	// option to indicate that each element in the slice returned by the
	// constructor should be injected into the value group individually.
	Group string

	// Target is the constructor or value being annotated with fx.Annotated.
	Target any
}

func (a Annotated) String() string {
	var fields []string
	if len(a.Name) > 0 {
		fields = append(fields, fmt.Sprintf("Name: %q", a.Name))
	}
	if len(a.Group) > 0 {
		fields = append(fields, fmt.Sprintf("Group: %q", a.Group))
	}
	if a.Target != nil {
		fields = append(fields, fmt.Sprintf("Target: %v", fxreflect.FuncName(a.Target)))
	}
	return fmt.Sprintf("fx.Annotated{%v}", strings.Join(fields, ", "))
}

var (
	// field used for embedding fx.In type in generated struct.
	_inAnnotationField = reflect.StructField{
		Name:      "In",
		Type:      reflect.TypeOf(In{}),
		Anonymous: true,
	}
	// field used for embedding fx.Out type in generated struct.
	_outAnnotationField = reflect.StructField{
		Name:      "Out",
		Type:      reflect.TypeOf(Out{}),
		Anonymous: true,
	}
)

// Annotation specifies how to wrap a target for [Annotate].
// It can be used to set up additional options for a constructor,
// or with [Supply], for a value.
type Annotation interface {
	apply(*annotated) error
	build(*annotated) (any, error)
}

var (
	_typeOfError = reflect.TypeOf((*error)(nil)).Elem()
	_nilError    = reflect.Zero(_typeOfError)
)

// annotationError is a wrapper for an error that was encountered while
// applying annotation to a function. It contains the specific error
// that it encountered as well as the target interface that was attempted
// to be annotated.
type annotationError struct {
	target any
	err    error
}

func (e *annotationError) Error() string {
	return e.err.Error()
}

// Unwrap the wrapped error.
func (e *annotationError) Unwrap() error {
	return e.err
}

type paramTagsAnnotation struct {
	tags []string
}

var _ Annotation = paramTagsAnnotation{}
var (
	errTagSyntaxSpace            = errors.New(`multiple tags are not separated by space`)
	errTagKeySyntax              = errors.New("tag key is invalid, Use group, name or optional as tag keys")
	errTagValueSyntaxQuote       = errors.New(`tag value should start with double quote. i.e. key:"value" `)
	errTagValueSyntaxEndingQuote = errors.New(`tag value should end in double quote. i.e. key:"value" `)
)

// Collections of key value pairs within a tag should be separated by a space.
// Eg: `group:"some" optional:"true"`.
func verifyTagsSpaceSeparated(tagIdx int, tag string) error {
	if tagIdx > 0 && tag != "" && tag[0] != ' ' {
		return errTagSyntaxSpace
	}
	return nil
}

// verify tag values are delimited with double quotes.
func verifyValueQuote(value string) (string, error) {
	// starting quote should be a double quote
	if value[0] != '"' {
		return "", errTagValueSyntaxQuote
	}
	// validate tag value is within quotes
	i := 1
	for i < len(value) && value[i] != '"' {
		if value[i] == '\\' {
			i++
		}
		i++
	}
	if i >= len(value) {
		return "", errTagValueSyntaxEndingQuote
	}
	return value[i+1:], nil
}

// Check whether the tag follows valid struct.
// format and returns an error if it's invalid. (i.e. not following
// tag:"value" space-separated list )
// Currently dig accepts only 'name', 'group', 'optional' as valid tag keys.
func verifyAnnotateTag(tag string) error {
	tagIdx := 0
	validKeys := map[string]struct{}{"group": {}, "optional": {}, "name": {}}
	for ; tag != ""; tagIdx++ {
		if err := verifyTagsSpaceSeparated(tagIdx, tag); err != nil {
			return err
		}
		i := 0
		if strings.TrimSpace(tag) == "" {
			return nil
		}
		// parsing the key i.e. till reaching colon :
		for i < len(tag) && tag[i] != ':' {
			i++
		}
		key := strings.TrimSpace(tag[:i])
		if _, ok := validKeys[key]; !ok {
			return errTagKeySyntax
		}
		value, err := verifyValueQuote(tag[i+1:])
		if err != nil {
			return err
		}
		tag = value
	}
	return nil
}

// Given func(T1, T2, T3, ..., TN), this generates a type roughly
// equivalent to,
//
//   struct {
//     fx.In
//
//     Field1 T1 `$tags[0]`
//     Field2 T2 `$tags[1]`
//     ...
//     FieldN TN `$tags[N-1]`
//   }
//
// If there has already been a ParamTag that was applied, this
// will return an error.
//
// If the tag is invalid and has mismatched quotation for example,
// (`tag_name:"tag_value') , this will return an error.

func (pt paramTagsAnnotation) apply(ann *annotated) error {
	if len(ann.ParamTags) > 0 {
		return errors.New("cannot apply more than one line of ParamTags")
	}
	for _, tag := range pt.tags {
		if err := verifyAnnotateTag(tag); err != nil {
			return err
		}
	}
	ann.ParamTags = pt.tags
	return nil
}

// build builds and returns a constructor after applying a ParamTags annotation
func (pt paramTagsAnnotation) build(ann *annotated) (any, error) {
	paramTypes, remap := pt.parameters(ann)
	resultTypes, _ := ann.currentResultTypes()

	origFn := reflect.ValueOf(ann.Target)
	newFnType := reflect.FuncOf(paramTypes, resultTypes, false)
	newFn := reflect.MakeFunc(newFnType, func(args []reflect.Value) []reflect.Value {
		args = remap(args)
		return origFn.Call(args)
	})
	return newFn.Interface(), nil
}

// parameters returns the type for the parameters of the annotated function,
// and a function that maps the arguments of the annotated function
// back to the arguments of the target function.
func (pt paramTagsAnnotation) parameters(ann *annotated) (
	types []reflect.Type,
	remap func([]reflect.Value) []reflect.Value,
) {
	ft := reflect.TypeOf(ann.Target)
	types = make([]reflect.Type, ft.NumIn())
	for i := 0; i < ft.NumIn(); i++ {
		types[i] = ft.In(i)
	}

	// No parameter annotations. Return the original types
	// and an identity function.
	if len(pt.tags) == 0 {
		return types, func(args []reflect.Value) []reflect.Value {
			return args
		}
	}

	// Turn parameters into an fx.In struct.
	inFields := []reflect.StructField{_inAnnotationField}

	// there was a variadic argument, so it was pre-transformed
	if len(types) > 0 && isIn(types[0]) {
		paramType := types[0]

		for i := 1; i < paramType.NumField(); i++ {
			origField := paramType.Field(i)
			field := reflect.StructField{
				Name: origField.Name,
				Type: origField.Type,
				Tag:  origField.Tag,
			}
			if i-1 < len(pt.tags) {
				field.Tag = reflect.StructTag(pt.tags[i-1])
			}

			inFields = append(inFields, field)
		}

		types = []reflect.Type{reflect.StructOf(inFields)}
		return types, func(args []reflect.Value) []reflect.Value {
			param := args[0]
			args[0] = reflect.New(paramType).Elem()
			for i := 1; i < paramType.NumField(); i++ {
				args[0].Field(i).Set(param.Field(i))
			}
			return args
		}
	}

	for i, t := range types {
		field := reflect.StructField{
			Name: fmt.Sprintf("Field%d", i),
			Type: t,
		}
		if i < len(pt.tags) {
			field.Tag = reflect.StructTag(pt.tags[i])
		}

		inFields = append(inFields, field)
	}

	types = []reflect.Type{reflect.StructOf(inFields)}
	return types, func(args []reflect.Value) []reflect.Value {
		params := args[0]
		args = args[:0]
		for i := 0; i < ft.NumIn(); i++ {
			args = append(args, params.Field(i+1))
		}
		return args
	}
}

// ParamTags is an Annotation that annotates the parameter(s) of a function.
//
// When multiple tags are specified, each tag is mapped to the corresponding
// positional parameter.
// For example, the following will refer to a named database connection,
// and the default, unnamed logger:
//
//	fx.Annotate(func(log *log.Logger, conn *sql.DB) *Handler {
//		// ...
//	}, fx.ParamTags("", `name:"ro"`))
//
// ParamTags cannot be used in a function that takes an fx.In struct as a
// parameter.
func ParamTags(tags ...string) Annotation {
	return paramTagsAnnotation{tags}
}

type resultTagsAnnotation struct {
	tags []string
}

var _ Annotation = resultTagsAnnotation{}

// Given func(T1, T2, T3, ..., TN), this generates a type roughly
// equivalent to,
//
//	struct {
//	  fx.Out
//
//	  Field1 T1 `$tags[0]`
//	  Field2 T2 `$tags[1]`
//	  ...
//	  FieldN TN `$tags[N-1]`
//	}
//
// If there has already been a ResultTag that was applied, this
// will return an error.
//
// If the tag is invalid and has mismatched quotation for example,
// (`tag_name:"tag_value') , this will return an error.
func (rt resultTagsAnnotation) apply(ann *annotated) error {
	if len(ann.ResultTags) > 0 {
		return errors.New("cannot apply more than one line of ResultTags")
	}
	for _, tag := range rt.tags {
		if err := verifyAnnotateTag(tag); err != nil {
			return err
		}
	}
	ann.ResultTags = rt.tags
	return nil
}

// build builds and returns a constructor after applying a ResultTags annotation
func (rt resultTagsAnnotation) build(ann *annotated) (any, error) {
	paramTypes := ann.currentParamTypes()
	resultTypes, remapResults := rt.results(ann)
	origFn := reflect.ValueOf(ann.Target)
	newFnType := reflect.FuncOf(paramTypes, resultTypes, false)
	newFn := reflect.MakeFunc(newFnType, func(args []reflect.Value) []reflect.Value {
		results := origFn.Call(args)
		return remapResults(results)
	})
	return newFn.Interface(), nil
}

// results returns the types of the results of the annotated function,
// and a function that maps the results of the target function,
// into a result compatible with the annotated function.
func (rt resultTagsAnnotation) results(ann *annotated) (
	types []reflect.Type,
	remap func([]reflect.Value) []reflect.Value,
) {
	types, hasError := ann.currentResultTypes()

	if hasError {
		types = types[:len(types)-1]
	}

	// No result annotations. Return the original types
	// and an identity function.
	if len(rt.tags) == 0 {
		return types, func(results []reflect.Value) []reflect.Value {
			return results
		}
	}

	// if there's no Out struct among the return types, there was no As annotation applied
	// just replace original result types with an Out struct and apply tags
	var (
		newOut       outStructInfo
		existingOuts []reflect.Type
	)

	newOut.Fields = []reflect.StructField{_outAnnotationField}
	newOut.Offsets = []int{}

	for i, t := range types {
		if !isOut(t) {
			// this must be from the original function.
			// apply the tags
			field := reflect.StructField{
				Name: fmt.Sprintf("Field%d", i),
				Type: t,
			}
			if i < len(rt.tags) {
				field.Tag = reflect.StructTag(rt.tags[i])
			}
			newOut.Offsets = append(newOut.Offsets, len(newOut.Fields))
			newOut.Fields = append(newOut.Fields, field)
			continue
		}
		// this must be from an As annotation
		// apply the tags to the existing type
		taggedFields := make([]reflect.StructField, t.NumField())
		taggedFields[0] = _outAnnotationField
		for j, tag := range rt.tags {
			if j+1 < t.NumField() {
				field := t.Field(j + 1)
				taggedFields[j+1] = reflect.StructField{
					Name: field.Name,
					Type: field.Type,
					Tag:  reflect.StructTag(tag),
				}
			}
		}
		existingOuts = append(existingOuts, reflect.StructOf(taggedFields))
	}

	resType := reflect.StructOf(newOut.Fields)

	outTypes := []reflect.Type{resType}
	// append existing outs back to outTypes
	outTypes = append(outTypes, existingOuts...)
	if hasError {
		outTypes = append(outTypes, _typeOfError)
	}

	return outTypes, func(results []reflect.Value) []reflect.Value {
		var (
			outErr     error
			outResults []reflect.Value
		)
		outResults = append(outResults, reflect.New(resType).Elem())

		tIdx := 0
		for i, r := range results {
			if i == len(results)-1 && hasError {
				// If hasError and this is the last item,
				// we are guaranteed that this is an error
				// object.
				if err, _ := r.Interface().(error); err != nil {
					outErr = err
				}
				continue
			}
			if i < len(newOut.Offsets) {
				if fieldIdx := newOut.Offsets[i]; fieldIdx > 0 {
					// fieldIdx 0 is an invalid index
					// because it refers to uninitialized
					// outs and would point to fx.Out in the
					// struct definition. We need to check this
					// to prevent panic from setting fx.Out to
					// a value.
					outResults[0].Field(fieldIdx).Set(r)
				}
				continue
			}
			if isOut(r.Type()) {
				tIdx++
				if tIdx < len(outTypes) {
					newResult := reflect.New(outTypes[tIdx]).Elem()
					for j := 1; j < outTypes[tIdx].NumField(); j++ {
						newResult.Field(j).Set(r.Field(j))
					}
					outResults = append(outResults, newResult)
				}
			}
		}

		if hasError {
			if outErr != nil {
				outResults = append(outResults, reflect.ValueOf(outErr))
			} else {
				outResults = append(outResults, _nilError)
			}
		}

		return outResults
	}
}

// ResultTags is an Annotation that annotates the result(s) of a function.
// When multiple tags are specified, each tag is mapped to the corresponding
// positional result.
//
// For example, the following will produce a named database connection.
//
//	fx.Annotate(func() (*sql.DB, error) {
//		// ...
//	}, fx.ResultTags(`name:"ro"`))
//
// ResultTags cannot be used on a function that returns an fx.Out struct.
func ResultTags(tags ...string) Annotation {
	return resultTagsAnnotation{tags}
}

type outStructInfo struct {
	Fields  []reflect.StructField // fields of the struct
	Offsets []int                 // Offsets[i] is the index of result i in Fields
}

type _lifecycleHookAnnotationType int

const (
	_unknownHookType _lifecycleHookAnnotationType = iota
	_onStartHookType
	_onStopHookType
)

type lifecycleHookAnnotation struct {
	Type   _lifecycleHookAnnotationType
	Target any
}

var _ Annotation = (*lifecycleHookAnnotation)(nil)

func (la *lifecycleHookAnnotation) String() string {
	name := "UnknownHookAnnotation"
	switch la.Type {
	case _onStartHookType:
		name = _onStartHook
	case _onStopHookType:
		name = _onStopHook
	}
	return name
}

func (la *lifecycleHookAnnotation) apply(ann *annotated) error {
	if la.Target == nil {
		return fmt.Errorf(
			"cannot use nil function for %q hook annotation",
			la,
		)
	}

	for _, h := range ann.Hooks {
		if la.Type == h.Type {
			return fmt.Errorf(
				"cannot apply more than one %q hook annotation",
				la,
			)
		}
	}

	ft := reflect.TypeOf(la.Target)

	if ft.Kind() != reflect.Func {
		return fmt.Errorf(
			"must provide function for %q hook, got %v (%T)",
			la,
			la.Target,
			la.Target,
		)
	}

	if n := ft.NumOut(); n > 0 {
		if n > 1 || ft.Out(0) != _typeOfError {
			return fmt.Errorf(
				"optional hook return may only be an error, got %v (%T)",
				la.Target,
				la.Target,
			)
		}
	}

	if ft.IsVariadic() {
		return fmt.Errorf(
			"hooks must not accept variadic parameters, got %v (%T)",
			la.Target,
			la.Target,
		)
	}

	ann.Hooks = append(ann.Hooks, la)
	return nil
}

// build builds and returns a constructor after applying a lifecycle hook annotation.
func (la *lifecycleHookAnnotation) build(ann *annotated) (any, error) {
	resultTypes, hasError := ann.currentResultTypes()
	if !hasError {
		resultTypes = append(resultTypes, _typeOfError)
	}

	hookInstaller, paramTypes, remapParams, err := la.buildHookInstaller(ann)
	if err != nil {
		return nil, err
	}

	origFn := reflect.ValueOf(ann.Target)
	newFnType := reflect.FuncOf(paramTypes, resultTypes, false)
	newFn := reflect.MakeFunc(newFnType, func(args []reflect.Value) []reflect.Value {
		// copy the original arguments before remapping the parameters
		// so that we can apply them to the hookInstaller.
		origArgs := make([]reflect.Value, len(args))
		copy(origArgs, args)
		args = remapParams(args)
		results := origFn.Call(args)
		if hasError {
			errVal := results[len(results)-1]
			results = results[:len(results)-1]
			if err, _ := errVal.Interface().(error); err != nil {
				// if constructor returned error, do not call hook installer
				return append(results, errVal)
			}
		}
		hookInstallerResults := hookInstaller.Call(append(results, origArgs...))
		results = append(results, hookInstallerResults[0])
		return results
	})
	return newFn.Interface(), nil
}

var (
	_typeOfLifecycle = reflect.TypeOf((*Lifecycle)(nil)).Elem()
	_typeOfContext   = reflect.TypeOf((*context.Context)(nil)).Elem()
)

// validateHookDeps validates the dependencies of a hook function and returns true if the dependencies are valid.
func (la *lifecycleHookAnnotation) validateHookDeps(hookParamTypes []reflect.Type, paramTypes []reflect.Type, resultTypes []reflect.Type) (err error) {
	type key struct {
		t     reflect.Type
		name  string
		group string
	}

	formatLog := func(k key) error {
		var tags []string
		if len(k.name) > 0 {
			tags = append(tags, fmt.Sprintf("name:\"%s\"", k.name))
		}
		if len(k.group) > 0 {
			tags = append(tags, fmt.Sprintf("group:\"%s\"", k.group))
		}
		var formatted string
		if len(tags) > 0 {
			formatted = fmt.Sprintf("%s `%s`", k.t.String(), strings.Join(tags, " "))
		} else {
			formatted = k.t.String()
		}
		return 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)
	}
	err = nil
	seen := make(map[key]struct{})

	for _, t := range paramTypes {
		if !isIn(t) {
			seen[key{t: t}] = struct{}{}
			continue
		}
		for i := 1; i < t.NumField(); i++ {
			field := t.Field(i)
			seen[key{
				t:     field.Type,
				name:  field.Tag.Get("name"),
				group: field.Tag.Get("group"),
			}] = struct{}{}
		}
	}
	for _, t := range resultTypes {
		if !isOut(t) {
			seen[key{t: t}] = struct{}{}
			continue
		}
		for i := 1; i < t.NumField(); i++ {
			field := t.Field(i)
			seen[key{
				t:     field.Type,
				name:  field.Tag.Get("name"),
				group: field.Tag.Get("group"),
			}] = struct{}{}
		}
	}
	for _, t := range hookParamTypes {
		if !isIn(t) {
			k := key{t: t}
			if _, ok := seen[k]; !ok {
				err = formatLog(k)
				return
			}
			continue
		}
		for i := 1; i < t.NumField(); i++ {
			field := t.Field(i)
			k := key{
				t:     field.Type,
				name:  field.Tag.Get("name"),
				group: field.Tag.Get("group"),
			}
			if _, ok := seen[k]; !ok {
				err = formatLog(k)
				return
			}
		}
	}
	return
}

// buildHookInstaller returns a function that appends a hook to Lifecycle when called,
// along with the new parameter types and a function that maps arguments to the annotated constructor
func (la *lifecycleHookAnnotation) buildHookInstaller(ann *annotated) (
	hookInstaller reflect.Value,
	paramTypes []reflect.Type,
	remapParams func([]reflect.Value) []reflect.Value, // function to remap parameters to function being annotated
	err error,
) {
	paramTypes = ann.currentParamTypes()
	paramTypes, remapParams = injectLifecycle(paramTypes)

	resultTypes, hasError := ann.currentResultTypes()
	if hasError {
		resultTypes = resultTypes[:len(resultTypes)-1]
	}

	// look for the context.Context type from the original hook function
	// and then exclude it from the paramTypes of invokeFn because context.Context
	// will be injected by the lifecycle
	ctxPos := -1
	ctxStructPos := -1
	origHookFn := reflect.ValueOf(la.Target)
	origHookFnT := reflect.TypeOf(la.Target)
	invokeParamTypes := []reflect.Type{
		_typeOfLifecycle,
	}
	for i := 0; i < origHookFnT.NumIn(); i++ {
		t := origHookFnT.In(i)
		if t == _typeOfContext && ctxPos < 0 {
			ctxPos = i
			continue
		}

		if !isIn(t) {
			invokeParamTypes = append(invokeParamTypes, origHookFnT.In(i))
			continue
		}
		fields := []reflect.StructField{_inAnnotationField}
		for j := 1; j < t.NumField(); j++ {
			field := t.Field(j)
			if field.Type == _typeOfContext && ctxPos < 0 {
				ctxStructPos = i
				ctxPos = j
				continue
			}
			fields = append(fields, field)
		}
		invokeParamTypes = append(invokeParamTypes, reflect.StructOf(fields))

	}
	if err = la.validateHookDeps(invokeParamTypes, paramTypes, resultTypes); err != nil {
		return
	}
	invokeFnT := reflect.FuncOf(invokeParamTypes, []reflect.Type{}, false)
	invokeFn := reflect.MakeFunc(invokeFnT, func(args []reflect.Value) (results []reflect.Value) {
		lc := args[0].Interface().(Lifecycle)
		args = args[1:]
		hookArgs := make([]reflect.Value, origHookFnT.NumIn())

		hookFn := func(ctx context.Context) (err error) {
			// If the hook function has multiple parameters, and the first
			// parameter is a context, inject the provided context.
			if ctxStructPos < 0 {
				offset := 0
				for i := range hookArgs {
					if i == ctxPos {
						hookArgs[i] = reflect.ValueOf(ctx)
						offset = 1
						continue
					}
					if i-offset >= 0 && i-offset < len(args) {
						hookArgs[i] = args[i-offset]
					}
				}
			} else {
				for i := 0; i < origHookFnT.NumIn(); i++ {
					if i != ctxStructPos {
						hookArgs[i] = args[i]
						continue
					}
					t := origHookFnT.In(i)
					v := reflect.New(t).Elem()
					for j := 1; j < t.NumField(); j++ {
						if j < ctxPos {
							v.Field(j).Set(args[i].Field(j))
						} else if j == ctxPos {
							v.Field(j).Set(reflect.ValueOf(ctx))
						} else {
							v.Field(j).Set(args[i].Field(j - 1))
						}
					}
					hookArgs[i] = v
				}
			}
			hookResults := origHookFn.Call(hookArgs)
			if len(hookResults) > 0 && hookResults[0].Type() == _typeOfError {
				err, _ = hookResults[0].Interface().(error)
			}
			return err
		}
		lc.Append(la.buildHook(hookFn))
		return results
	})

	installerType := reflect.FuncOf(append(resultTypes, paramTypes...), []reflect.Type{_typeOfError}, false)
	hookInstaller = reflect.MakeFunc(installerType, func(args []reflect.Value) (results []reflect.Value) {
		// build a private scope for hook function
		var scope *dig.Scope
		switch la.Type {
		case _onStartHookType:
			scope = ann.container.Scope("onStartHookScope")
		case _onStopHookType:
			scope = ann.container.Scope("onStopHookScope")
		}

		// provide the private scope with the current dependencies and results of the annotated function
		results = []reflect.Value{_nilError}
		ctor := makeHookScopeCtor(paramTypes, resultTypes, args)
		if err := scope.Provide(ctor); err != nil {
			results[0] = reflect.ValueOf(fmt.Errorf("error providing possible parameters for hook installer: %w", err))
			return results
		}

		// invoking invokeFn appends the hook function to lifecycle
		if err := scope.Invoke(invokeFn.Interface()); err != nil {
			results[0] = reflect.ValueOf(fmt.Errorf("error invoking hook installer: %w", err))
			return results
		}
		return results
	})
	return hookInstaller, paramTypes, remapParams, nil
}

var (
	_nameTag  = "name"
	_groupTag = "group"
)

// makeHookScopeCtor makes a constructor that provides all possible parameters
// that the lifecycle hook being appended can depend on. It also deduplicates
// duplicate param and result types, which is possible when using fx.Decorate,
// and uses values from results for providing the deduplicated types.
func makeHookScopeCtor(paramTypes []reflect.Type, resultTypes []reflect.Type, args []reflect.Value) any {
	type key struct {
		t     reflect.Type
		name  string
		group string
	}
	seen := map[key]struct{}{}
	outTypes := make([]reflect.Type, len(resultTypes))
	for i, t := range resultTypes {
		outTypes[i] = t
		if isOut(t) {
			for j := 1; j < t.NumField(); j++ {
				field := t.Field(j)
				seen[key{
					t:     field.Type,
					name:  field.Tag.Get(_nameTag),
					group: field.Tag.Get(_groupTag),
				}] = struct{}{}
			}
			continue
		}
		seen[key{t: t}] = struct{}{}
	}

	fields := []reflect.StructField{_outAnnotationField}

	skippedParams := make([][]int, len(paramTypes))

	for i, t := range paramTypes {
		skippedParams[i] = []int{}
		if isIn(t) {
			for j := 1; j < t.NumField(); j++ {
				origField := t.Field(j)
				k := key{
					t:     origField.Type,
					name:  origField.Tag.Get(_nameTag),
					group: origField.Tag.Get(_groupTag),
				}

				if _, ok := seen[k]; ok {
					skippedParams[i] = append(skippedParams[i], j)
					continue
				}

				field := reflect.StructField{
					Name: fmt.Sprintf("Field%d", j-1),
					Type: origField.Type,
					Tag:  origField.Tag,
				}
				fields = append(fields, field)
			}
			continue
		}
		k := key{t: t}

		if _, ok := seen[k]; ok {
			skippedParams[i] = append(skippedParams[i], i)
			continue
		}
		field := reflect.StructField{
			Name: fmt.Sprintf("Field%d", i),
			Type: t,
		}
		fields = append(fields, field)
	}

	outTypes = append(outTypes, reflect.StructOf(fields))
	ctorType := reflect.FuncOf([]reflect.Type{}, outTypes, false)
	ctor := reflect.MakeFunc(ctorType, func(_ []reflect.Value) []reflect.Value {
		nOut := len(outTypes)
		results := make([]reflect.Value, nOut)
		for i := 0; i < nOut-1; i++ {
			results[i] = args[i]
		}

		v := reflect.New(outTypes[nOut-1]).Elem()
		fieldIdx := 1
		for i := nOut - 1; i < len(args); i++ {
			paramIdx := i - (nOut - 1)
			if isIn(paramTypes[paramIdx]) {
				skippedIdx := 0
				for j := 1; j < paramTypes[paramIdx].NumField(); j++ {
					if len(skippedParams[paramIdx]) > 0 && skippedParams[paramIdx][skippedIdx] == j {
						// skip
						skippedIdx++
						continue
					}
					v.Field(fieldIdx).Set(args[i].Field(j))
					fieldIdx++
				}
			} else {
				if len(skippedParams[paramIdx]) > 0 && skippedParams[paramIdx][0] == paramIdx {
					continue
				}
				v.Field(fieldIdx).Set(args[i])
				fieldIdx++
			}
		}
		results[nOut-1] = v

		return results
	})
	return ctor.Interface()
}

func injectLifecycle(paramTypes []reflect.Type) ([]reflect.Type, func([]reflect.Value) []reflect.Value) {
	// since lifecycle already exists in param types, no need to inject again
	if lifecycleExists(paramTypes) {
		return paramTypes, func(args []reflect.Value) []reflect.Value {
			return args
		}
	}
	// If params are tagged or there's an untagged variadic argument,
	// add a Lifecycle field to the param struct
	if len(paramTypes) > 0 && isIn(paramTypes[0]) {
		taggedParam := paramTypes[0]
		fields := []reflect.StructField{
			taggedParam.Field(0),
			{
				Name: "Lifecycle",
				Type: _typeOfLifecycle,
			},
		}
		for i := 1; i < taggedParam.NumField(); i++ {
			fields = append(fields, taggedParam.Field(i))
		}
		newParamType := reflect.StructOf(fields)
		return []reflect.Type{newParamType}, func(args []reflect.Value) []reflect.Value {
			param := args[0]
			args[0] = reflect.New(taggedParam).Elem()
			for i := 1; i < taggedParam.NumField(); i++ {
				args[0].Field(i).Set(param.Field(i + 1))
			}
			return args
		}
	}

	return append([]reflect.Type{_typeOfLifecycle}, paramTypes...), func(args []reflect.Value) []reflect.Value {
		return args[1:]
	}
}

func lifecycleExists(paramTypes []reflect.Type) bool {
	for _, t := range paramTypes {
		if t == _typeOfLifecycle {
			return true
		}
		if isIn(t) {
			for i := 1; i < t.NumField(); i++ {
				if t.Field(i).Type == _typeOfLifecycle {
					return true
				}
			}
		}
	}
	return false
}

func (la *lifecycleHookAnnotation) buildHook(fn func(context.Context) error) (hook Hook) {
	switch la.Type {
	case _onStartHookType:
		hook.OnStart = fn
	case _onStopHookType:
		hook.OnStop = fn
	}
	return hook
}

// OnStart is an Annotation that appends an OnStart Hook to the application
// Lifecycle when that function is called. This provides a way to create
// Lifecycle OnStart (see Lifecycle type documentation) hooks without building a
// function that takes a dependency on the Lifecycle type.
//
//	fx.Provide(
//		fx.Annotate(
//			NewServer,
//			fx.OnStart(func(ctx context.Context, server Server) error {
//				return server.Listen(ctx)
//			}),
//		)
//	)
//
// Which is functionally the same as:
//
//	 fx.Provide(
//	   func(lifecycle fx.Lifecycle, p Params) Server {
//	     server := NewServer(p)
//	     lifecycle.Append(fx.Hook{
//		      OnStart: func(ctx context.Context) error {
//			    return server.Listen(ctx)
//		      },
//	     })
//		 return server
//	   }
//	 )
//
// It is also possible to use OnStart annotation with other parameter and result
// annotations, provided that the parameter of the function passed to OnStart
// matches annotated parameters and results.
//
// For example, the following is possible:
//
//	fx.Provide(
//		fx.Annotate(
//			func (a A) B {...},
//			fx.ParamTags(`name:"A"`),
//			fx.ResultTags(`name:"B"`),
//			fx.OnStart(func (p OnStartParams) {...}),
//		),
//	)
//
// As long as OnStartParams looks like the following and has no other dependencies
// besides Context or Lifecycle:
//
//	type OnStartParams struct {
//		fx.In
//		FieldA A `name:"A"`
//		FieldB B `name:"B"`
//	}
//
// Only one OnStart annotation may be applied to a given function at a time,
// however functions may be annotated with other types of lifecycle Hooks, such
// as OnStop. The hook function passed into OnStart cannot take any arguments
// outside of the annotated constructor's existing dependencies or results, except
// a context.Context.
func OnStart(onStart any) Annotation {
	return &lifecycleHookAnnotation{
		Type:   _onStartHookType,
		Target: onStart,
	}
}

// OnStop is an Annotation that appends an OnStop Hook to the application
// Lifecycle when that function is called. This provides a way to create
// Lifecycle OnStop (see Lifecycle type documentation) hooks without building a
// function that takes a dependency on the Lifecycle type.
//
//	fx.Provide(
//		fx.Annotate(
//			NewServer,
//			fx.OnStop(func(ctx context.Context, server Server) error {
//				return server.Shutdown(ctx)
//			}),
//		)
//	)
//
// Which is functionally the same as:
//
//	 fx.Provide(
//	   func(lifecycle fx.Lifecycle, p Params) Server {
//	     server := NewServer(p)
//	     lifecycle.Append(fx.Hook{
//		      OnStop: func(ctx context.Context) error {
//			    return server.Shutdown(ctx)
//		      },
//	     })
//		 return server
//	   }
//	 )
//
// It is also possible to use OnStop annotation with other parameter and result
// annotations, provided that the parameter of the function passed to OnStop
// matches annotated parameters and results.
//
// For example, the following is possible:
//
//	fx.Provide(
//		fx.Annotate(
//			func (a A) B {...},
//			fx.ParamTags(`name:"A"`),
//			fx.ResultTags(`name:"B"`),
//			fx.OnStop(func (p OnStopParams) {...}),
//		),
//	)
//
// As long as OnStopParams looks like the following and has no other dependencies
// besides Context or Lifecycle:
//
//	type OnStopParams struct {
//		fx.In
//		FieldA A `name:"A"`
//		FieldB B `name:"B"`
//	}
//
// Only one OnStop annotation may be applied to a given function at a time,
// however functions may be annotated with other types of lifecycle Hooks, such
// as OnStart. The hook function passed into OnStop cannot take any arguments
// outside of the annotated constructor's existing dependencies or results, except
// a context.Context.
func OnStop(onStop any) Annotation {
	return &lifecycleHookAnnotation{
		Type:   _onStopHookType,
		Target: onStop,
	}
}

type asAnnotation struct {
	targets []any
	types   []asType
}

type asType struct {
	self bool
	typ  reflect.Type // May be nil if self is true.
}

func (a asType) String() string {
	if a.self {
		return "self"
	}
	return a.typ.String()
}

func isOut(t reflect.Type) bool {
	return (t.Kind() == reflect.Struct &&
		dig.IsOut(reflect.New(t).Elem().Interface()))
}

func isIn(t reflect.Type) bool {
	return (t.Kind() == reflect.Struct &&
		dig.IsIn(reflect.New(t).Elem().Interface()))
}

var _ Annotation = (*asAnnotation)(nil)

// As is an Annotation that annotates the result of a function (i.e. a
// constructor) to be provided as another interface.
//
// For example, the following code specifies that the return type of
// bytes.NewBuffer (bytes.Buffer) should be provided as io.Writer type:
//
//	fx.Provide(
//	  fx.Annotate(bytes.NewBuffer, fx.As(new(io.Writer)))
//	)
//
// In other words, the code above is equivalent to:
//
//	fx.Provide(func() io.Writer {
//	  return bytes.NewBuffer()
//	  // provides io.Writer instead of *bytes.Buffer
//	})
//
// Note that the bytes.Buffer type is provided as an io.Writer type, so this
// constructor does NOT provide both bytes.Buffer and io.Writer type; it just
// provides io.Writer type.
//
// When multiple values are returned by the annotated function, each type
// gets mapped to corresponding positional result of the annotated function.
//
// For example,
//
//	func a() (bytes.Buffer, bytes.Buffer) {
//	  ...
//	}
//	fx.Provide(
//	  fx.Annotate(a, fx.As(new(io.Writer), new(io.Reader)))
//	)
//
// Is equivalent to,
//
//	fx.Provide(func() (io.Writer, io.Reader) {
//	  w, r := a()
//	  return w, r
//	}
//
// As entirely replaces the default return types of a function. In order
// to maintain the original return types when using As, see [Self].
//
// As annotation cannot be used in a function that returns an [Out] struct as a return type.
func As(interfaces ...any) Annotation {
	return &asAnnotation{targets: interfaces}
}

// Self returns a special value that can be passed to [As] to indicate
// that a type should be provided as its original type, in addition to whatever other
// types it gets provided as via other [As] annotations.
//
// For example,
//
//	fx.Provide(
//	  fx.Annotate(
//	    bytes.NewBuffer,
//	    fx.As(new(io.Writer)),
//	    fx.As(fx.Self()),
//	  )
//	)
//
// Is equivalent to,
//
//	fx.Provide(
//	  bytes.NewBuffer,
//	  func(b *bytes.Buffer) io.Writer {
//	    return b
//	  },
//	)
//
// in that it provides the same *bytes.Buffer instance
// as both a *bytes.Buffer and an io.Writer.
func Self() any {
	return &self{}
}

type self struct{}

func (at *asAnnotation) apply(ann *annotated) error {
	at.types = make([]asType, len(at.targets))
	for i, typ := range at.targets {
		if _, ok := typ.(*self); ok {
			at.types[i] = asType{self: true}
			continue
		}
		t := reflect.TypeOf(typ)
		if t.Kind() != reflect.Ptr || t.Elem().Kind() != reflect.Interface {
			return fmt.Errorf("fx.As: argument must be a pointer to an interface: got %v", t)
		}
		t = t.Elem()
		at.types[i] = asType{typ: t}
	}

	ann.As = append(ann.As, at.types)
	return nil
}

// build implements Annotation
func (at *asAnnotation) build(ann *annotated) (any, error) {
	paramTypes := ann.currentParamTypes()

	resultTypes, remapResults, err := at.results(ann)
	if err != nil {
		return nil, err
	}

	origFn := reflect.ValueOf(ann.Target)
	newFnType := reflect.FuncOf(paramTypes, resultTypes, false)
	newFn := reflect.MakeFunc(newFnType, func(args []reflect.Value) []reflect.Value {
		results := origFn.Call(args)
		return remapResults(results)
	})
	return newFn.Interface(), nil
}

func (at *asAnnotation) results(ann *annotated) (
	types []reflect.Type,
	remap func([]reflect.Value) []reflect.Value,
	err error,
) {
	types, hasError := ann.currentResultTypes()
	fields := []reflect.StructField{_outAnnotationField}
	if hasError {
		types = types[:len(types)-1]
	}
	resultFields, getResult := extractResultFields(types)

	for i, f := range resultFields {
		t := f.Type
		field := reflect.StructField{
			Name: fmt.Sprintf("Field%d", i),
			Type: t,
			Tag:  f.Tag,
		}

		if i >= len(at.types) || at.types[i].self {
			fields = append(fields, field)
			continue
		}

		if !t.Implements(at.types[i].typ) {
			return nil, nil, fmt.Errorf("invalid fx.As: %v does not implement %v", t, at.types[i])
		}
		field.Type = at.types[i].typ
		fields = append(fields, field)
	}
	resType := reflect.StructOf(fields)

	var outTypes []reflect.Type
	outTypes = append(types, resType)
	if hasError {
		outTypes = append(outTypes, _typeOfError)
	}

	return outTypes, func(results []reflect.Value) []reflect.Value {
		var (
			outErr     error
			outResults []reflect.Value
		)

		for i, r := range results {
			if i == len(results)-1 && hasError {
				// If hasError and this is the last item,
				// we are guaranteed that this is an error
				// object.
				if err, _ := r.Interface().(error); err != nil {
					outErr = err
				}
				continue
			}
			outResults = append(outResults, r)
		}

		newOutResult := reflect.New(resType).Elem()
		for i := 1; i < resType.NumField(); i++ {
			newOutResult.Field(i).Set(getResult(i, results))
		}
		outResults = append(outResults, newOutResult)

		if hasError {
			if outErr != nil {
				outResults = append(outResults, reflect.ValueOf(outErr))
			} else {
				outResults = append(outResults, _nilError)
			}
		}

		return outResults
	}, nil
}

func extractResultFields(types []reflect.Type) ([]reflect.StructField, func(int, []reflect.Value) reflect.Value) {
	var resultFields []reflect.StructField
	if len(types) > 0 && isOut(types[0]) {
		for i := 1; i < types[0].NumField(); i++ {
			resultFields = append(resultFields, types[0].Field(i))
		}
		return resultFields, func(idx int, results []reflect.Value) reflect.Value {
			return results[0].Field(idx)
		}
	}
	for i, t := range types {
		if isOut(t) {
			continue
		}
		field := reflect.StructField{
			Name: fmt.Sprintf("Field%d", i),
			Type: t,
		}
		resultFields = append(resultFields, field)
	}
	return resultFields, func(idx int, results []reflect.Value) reflect.Value {
		return results[idx-1]
	}
}

type fromAnnotation struct {
	targets []any
	types   []reflect.Type
}

var _ Annotation = (*fromAnnotation)(nil)

// From is an [Annotation] that annotates the parameter(s) for a function (i.e. a
// constructor) to be accepted from other provided types. It is analogous to the
// [As] for parameter types to the constructor.
//
// For example,
//
//	type Runner interface { Run() }
//	func NewFooRunner() *FooRunner // implements Runner
//	func NewRunnerWrap(r Runner) *RunnerWrap
//
//	fx.Provide(
//	  fx.Annotate(
//	    NewRunnerWrap,
//	    fx.From(new(*FooRunner)),
//	  ),
//	)
//
// Is equivalent to,
//
//	fx.Provide(func(r *FooRunner) *RunnerWrap {
//	  // need *FooRunner instead of Runner
//	  return NewRunnerWrap(r)
//	})
//
// When the annotated function takes in multiple parameters, each type gets
// mapped to corresponding positional parameter of the annotated function
//
// For example,
//
//	func NewBarRunner() *BarRunner // implements Runner
//	func NewRunnerWraps(r1 Runner, r2 Runner) *RunnerWraps
//
//	fx.Provide(
//	  fx.Annotate(
//	    NewRunnerWraps,
//	    fx.From(new(*FooRunner), new(*BarRunner)),
//	  ),
//	)
//
// Is equivalent to,
//
//	fx.Provide(func(r1 *FooRunner, r2 *BarRunner) *RunnerWraps {
//	  return NewRunnerWraps(r1, r2)
//	})
//
// From annotation cannot be used in a function that takes an [In] struct as a
// parameter.
func From(interfaces ...any) Annotation {
	return &fromAnnotation{targets: interfaces}
}

func (fr *fromAnnotation) apply(ann *annotated) error {
	if len(ann.From) > 0 {
		return errors.New("cannot apply more than one line of From")
	}
	ft := reflect.TypeOf(ann.Target)
	fr.types = make([]reflect.Type, len(fr.targets))
	for i, typ := range fr.targets {
		if ft.IsVariadic() && i == ft.NumIn()-1 {
			return errors.New("fx.From: cannot annotate a variadic argument")
		}
		t := reflect.TypeOf(typ)
		if t == nil || t.Kind() != reflect.Ptr {
			return fmt.Errorf("fx.From: argument must be a pointer to a type that implements some interface: got %v", t)
		}
		fr.types[i] = t.Elem()
	}
	ann.From = fr.types
	return nil
}

// build builds and returns a constructor after applying a From annotation
func (fr *fromAnnotation) build(ann *annotated) (any, error) {
	paramTypes, remap, err := fr.parameters(ann)
	if err != nil {
		return nil, err
	}
	resultTypes, _ := ann.currentResultTypes()

	origFn := reflect.ValueOf(ann.Target)
	newFnType := reflect.FuncOf(paramTypes, resultTypes, false)
	newFn := reflect.MakeFunc(newFnType, func(args []reflect.Value) []reflect.Value {
		args = remap(args)
		return origFn.Call(args)
	})
	return newFn.Interface(), nil
}

// parameters returns the type for the parameters of the annotated function,
// and a function that maps the arguments of the annotated function
// back to the arguments of the target function.
func (fr *fromAnnotation) parameters(ann *annotated) (
	types []reflect.Type,
	remap func([]reflect.Value) []reflect.Value,
	err error,
) {
	ft := reflect.TypeOf(ann.Target)
	types = make([]reflect.Type, ft.NumIn())
	for i := 0; i < ft.NumIn(); i++ {
		types[i] = ft.In(i)
	}

	// No parameter annotations. Return the original types
	// and an identity function.
	if len(fr.targets) == 0 {
		return types, func(args []reflect.Value) []reflect.Value {
			return args
		}, nil
	}

	// Turn parameters into an fx.In struct.
	inFields := []reflect.StructField{_inAnnotationField}

	// The following situations may occur:
	// 1. there was a variadic argument, so it was pre-transformed.
	// 2. another parameter annotation was transformed (ex: ParamTags).
	// so need to visit fields of the fx.In struct.
	if len(types) > 0 && isIn(types[0]) {
		paramType := types[0]

		for i := 1; i < paramType.NumField(); i++ {
			origField := paramType.Field(i)
			field := reflect.StructField{
				Name: origField.Name,
				Type: origField.Type,
				Tag:  origField.Tag,
			}
			if i-1 < len(fr.types) {
				t := fr.types[i-1]
				if !t.Implements(field.Type) {
					return nil, nil, fmt.Errorf("invalid fx.From: %v does not implement %v", t, field.Type)
				}
				field.Type = t
			}

			inFields = append(inFields, field)
		}

		types = []reflect.Type{reflect.StructOf(inFields)}
		return types, func(args []reflect.Value) []reflect.Value {
			param := args[0]
			args[0] = reflect.New(paramType).Elem()
			for i := 1; i < paramType.NumField(); i++ {
				args[0].Field(i).Set(param.Field(i))
			}
			return args
		}, nil
	}

	for i, t := range types {
		field := reflect.StructField{
			Name: fmt.Sprintf("Field%d", i),
			Type: t,
		}
		if i < len(fr.types) {
			t := fr.types[i]
			if !t.Implements(field.Type) {
				return nil, nil, fmt.Errorf("invalid fx.From: %v does not implement %v", t, field.Type)
			}
			field.Type = t
		}

		inFields = append(inFields, field)
	}

	types = []reflect.Type{reflect.StructOf(inFields)}
	return types, func(args []reflect.Value) []reflect.Value {
		params := args[0]
		args = args[:0]
		for i := 0; i < ft.NumIn(); i++ {
			args = append(args, params.Field(i+1))
		}
		return args
	}, nil
}

type annotated struct {
	Target      any
	Annotations []Annotation
	ParamTags   []string
	ResultTags  []string
	As          [][]asType
	From        []reflect.Type
	FuncPtr     uintptr
	Hooks       []*lifecycleHookAnnotation
	// container is used to build private scopes for lifecycle hook functions
	// added via fx.OnStart and fx.OnStop annotations.
	container *dig.Container
}

func (ann annotated) String() string {
	var sb strings.Builder
	sb.WriteString("fx.Annotate(")
	sb.WriteString(fxreflect.FuncName(ann.Target))
	if tags := ann.ParamTags; len(tags) > 0 {
		fmt.Fprintf(&sb, ", fx.ParamTags(%q)", tags)
	}
	if tags := ann.ResultTags; len(tags) > 0 {
		fmt.Fprintf(&sb, ", fx.ResultTags(%q)", tags)
	}
	if as := ann.As; len(as) > 0 {
		fmt.Fprintf(&sb, ", fx.As(%v)", as)
	}
	if from := ann.From; len(from) > 0 {
		fmt.Fprintf(&sb, ", fx.From(%v)", from)
	}
	return sb.String()
}

// Build builds and returns a constructor based on fx.In/fx.Out params and
// results wrapping the original constructor passed to fx.Annotate.
func (ann *annotated) Build() (any, error) {
	ann.container = dig.New()
	ft := reflect.TypeOf(ann.Target)
	if ft.Kind() != reflect.Func {
		return nil, fmt.Errorf("must provide constructor function, got %v (%T)", ann.Target, ann.Target)
	}

	if err := ann.typeCheckOrigFn(); err != nil {
		return nil, fmt.Errorf("invalid annotation function %T: %w", ann.Target, err)
	}

	ann.applyOptionalTag()

	var (
		err        error
		lcHookAnns []*lifecycleHookAnnotation
	)
	for _, annotation := range ann.Annotations {
		if lcHookAnn, ok := annotation.(*lifecycleHookAnnotation); ok {
			lcHookAnns = append(lcHookAnns, lcHookAnn)
			continue
		}
		if ann.Target, err = annotation.build(ann); err != nil {
			return nil, err
		}
	}

	// need to call cleanUpAsResults before applying lifecycle annotations
	// to exclude the original results from the hook's scope if any
	// fx.As annotations were applied
	ann.cleanUpAsResults()

	for _, la := range lcHookAnns {
		if ann.Target, err = la.build(ann); err != nil {
			return nil, err
		}
	}
	return ann.Target, nil
}

// applyOptionalTag checks if function being annotated is variadic
// and applies optional tag to the variadic argument before
// applying any other annotations
func (ann *annotated) applyOptionalTag() {
	ft := reflect.TypeOf(ann.Target)
	if !ft.IsVariadic() {
		return
	}

	resultTypes, _ := ann.currentResultTypes()

	fields := []reflect.StructField{_inAnnotationField}
	for i := 0; i < ft.NumIn(); i++ {
		field := reflect.StructField{
			Name: fmt.Sprintf("Field%d", i),
			Type: ft.In(i),
		}
		if i == ft.NumIn()-1 {
			// Mark a variadic argument optional by default
			// so that just wrapping a function in fx.Annotate does not
			// suddenly introduce a required []arg dependency.
			field.Tag = reflect.StructTag(`optional:"true"`)
		}
		fields = append(fields, field)
	}
	paramType := reflect.StructOf(fields)
	origFn := reflect.ValueOf(ann.Target)
	newFnType := reflect.FuncOf([]reflect.Type{paramType}, resultTypes, false)
	newFn := reflect.MakeFunc(newFnType, func(args []reflect.Value) []reflect.Value {
		params := args[0]
		args = args[:0]
		for i := 0; i < ft.NumIn(); i++ {
			args = append(args, params.Field(i+1))
		}
		return origFn.CallSlice(args)
	})
	ann.Target = newFn.Interface()
}

// cleanUpAsResults does a check to see if an As annotation was applied.
// If there was any fx.As annotation applied, cleanUpAsResults wraps the
// function one more time to remove the results from the original function.
func (ann *annotated) cleanUpAsResults() {
	// clean up orig function results if there were any As annotations
	if len(ann.As) < 1 {
		return
	}
	paramTypes := ann.currentParamTypes()
	resultTypes, hasError := ann.currentResultTypes()
	numRes := len(ann.As)
	if hasError {
		numRes++
	}
	newResultTypes := resultTypes[len(resultTypes)-numRes:]
	origFn := reflect.ValueOf(ann.Target)
	newFnType := reflect.FuncOf(paramTypes, newResultTypes, false)
	newFn := reflect.MakeFunc(newFnType, func(args []reflect.Value) (results []reflect.Value) {
		results = origFn.Call(args)
		results = results[len(results)-numRes:]
		return
	})
	ann.Target = newFn.Interface()
}

// checks and returns a non-nil error if the target function:
// - returns an fx.Out struct as a result and has either a ResultTags or an As annotation
// - takes in an fx.In struct as a parameter and has either a ParamTags or a From annotation
// - has an error result not as the last result.
func (ann *annotated) typeCheckOrigFn() error {
	ft := reflect.TypeOf(ann.Target)
	numOut := ft.NumOut()
	for i := range numOut {
		ot := ft.Out(i)
		if ot == _typeOfError && i != numOut-1 {
			return fmt.Errorf(
				"only the last result can be an error: "+
					"%v (%v) returns error as result %d",
				fxreflect.FuncName(ann.Target), ft, i)
		}
		if ot.Kind() != reflect.Struct {
			continue
		}
		if !dig.IsOut(reflect.New(ft.Out(i)).Elem().Interface()) {
			continue
		}
		if len(ann.ResultTags) > 0 || len(ann.As) > 0 {
			return errors.New("fx.Out structs cannot be annotated with fx.ResultTags or fx.As")
		}
	}
	for i := 0; i < ft.NumIn(); i++ {
		it := ft.In(i)
		if it.Kind() != reflect.Struct {
			continue
		}
		if !dig.IsIn(reflect.New(ft.In(i)).Elem().Interface()) {
			continue
		}
		if len(ann.ParamTags) > 0 || len(ann.From) > 0 {
			return errors.New("fx.In structs cannot be annotated with fx.ParamTags or fx.From")
		}
	}
	return nil
}

func (ann *annotated) currentResultTypes() (resultTypes []reflect.Type, hasError bool) {
	ft := reflect.TypeOf(ann.Target)
	numOut := ft.NumOut()
	resultTypes = make([]reflect.Type, numOut)

	for i := range numOut {
		resultTypes[i] = ft.Out(i)
		if resultTypes[i] == _typeOfError && i == numOut-1 {
			hasError = true
		}
	}
	return resultTypes, hasError
}

func (ann *annotated) currentParamTypes() []reflect.Type {
	ft := reflect.TypeOf(ann.Target)
	paramTypes := make([]reflect.Type, ft.NumIn())

	for i := 0; i < ft.NumIn(); i++ {
		paramTypes[i] = ft.In(i)
	}
	return paramTypes
}

// Annotate lets you annotate a function's parameters and returns
// without you having to declare separate struct definitions for them.
//
// For example,
//
//	func NewGateway(ro, rw *db.Conn) *Gateway { ... }
//	fx.Provide(
//	  fx.Annotate(
//	    NewGateway,
//	    fx.ParamTags(`name:"ro" optional:"true"`, `name:"rw"`),
//	    fx.ResultTags(`name:"foo"`),
//	  ),
//	)
//
// Is equivalent to,
//
//	type params struct {
//	  fx.In
//
//	  RO *db.Conn `name:"ro" optional:"true"`
//	  RW *db.Conn `name:"rw"`
//	}
//
//	type result struct {
//	  fx.Out
//
//	  GW *Gateway `name:"foo"`
//	}
//
//	fx.Provide(func(p params) result {
//	   return result{GW: NewGateway(p.RO, p.RW)}
//	})
//
// Using the same annotation multiple times is invalid.
// For example, the following will fail with an error:
//
//	fx.Provide(
//	  fx.Annotate(
//	    NewGateWay,
//	    fx.ParamTags(`name:"ro" optional:"true"`),
//	    fx.ParamTags(`name:"rw"), // ERROR: ParamTags was already used above
//	    fx.ResultTags(`name:"foo"`)
//	  )
//	)
//
// If more tags are given than the number of parameters/results, only
// the ones up to the number of parameters/results will be applied.
//
// # Variadic functions
//
// If the provided function is variadic, Annotate treats its parameter as a
// slice. For example,
//
//	fx.Annotate(func(w io.Writer, rs ...io.Reader) {
//	  // ...
//	}, ...)
//
// Is equivalent to,
//
//	fx.Annotate(func(w io.Writer, rs []io.Reader) {
//	  // ...
//	}, ...)
//
// You can use variadic parameters with Fx's value groups.
// For example,
//
//	fx.Annotate(func(mux *http.ServeMux, handlers ...http.Handler) {
//	  // ...
//	}, fx.ParamTags(``, `group:"server"`))
//
// If we provide the above to the application,
// any constructor in the Fx application can inject its HTTP handlers
// by using [Annotate], [Annotated], or [Out].
//
//	fx.Annotate(
//	  func(..) http.Handler { ... },
//	  fx.ResultTags(`group:"server"`),
//	)
//
//	fx.Annotated{
//	  Target: func(..) http.Handler { ... },
//	  Group:  "server",
//	}
func Annotate(t any, anns ...Annotation) any {
	result := annotated{Target: t}
	for _, ann := range anns {
		if err := ann.apply(&result); err != nil {
			return annotationError{
				target: t,
				err:    err,
			}
		}
	}
	result.Annotations = anns
	return result
}


================================================
FILE: annotated_test.go
================================================
// Copyright (c) 2019-2021 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

package fx_test

import (
	"bytes"
	"context"
	"errors"
	"fmt"
	"io"
	"strconv"
	"strings"
	"sync/atomic"
	"testing"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
	"go.uber.org/fx"
	"go.uber.org/fx/fxevent"
	"go.uber.org/fx/fxtest"
)

func TestAnnotated(t *testing.T) {
	t.Parallel()

	type a struct {
		name string
	}
	type in struct {
		fx.In

		A *a `name:"foo"`
	}
	newA := func() *a {
		return &a{name: "foo"}
	}
	t.Run("Provide", func(t *testing.T) {
		t.Parallel()

		var in in
		app := fxtest.New(t,
			fx.Provide(
				fx.Annotated{
					Name:   "foo",
					Target: newA,
				},
			),
			fx.Populate(&in),
		)
		defer app.RequireStart().RequireStop()
		assert.NotNil(t, in.A, "expected in.A to be injected")
		assert.Equal(t, "foo", in.A.name, "expected to get a type 'a' of name 'foo'")
	})
}

type fromStringer struct {
	name string
}

func (from *fromStringer) String() string {
	return from.name
}

type asStringer struct {
	name string
}

func (as *asStringer) String() string {
	return as.name
}

type anotherStringer struct {
	name string
}

func (a anotherStringer) String() string {
	return a.name
}

func TestAnnotatedFrom(t *testing.T) {
	t.Parallel()
	type myStringer interface {
		String() string
	}

	newFromStringer := func() *fromStringer {
		return &fromStringer{
			name: "a good stringer",
		}
	}

	tests := []struct {
		desc    string
		provide fx.Option
		invoke  any
	}{
		{
			desc: "provide a good stringer",
			provide: fx.Provide(
				newFromStringer,
				fx.Annotate(
					func(myStringer myStringer) fmt.Stringer {
						return &fromStringer{
							name: myStringer.String(),
						}
					},
					fx.From(new(*fromStringer)),
				),
			),
			invoke: func(s fmt.Stringer) {
				assert.Equal(t, s.String(), "a good stringer")
			},
		},
		{
			desc: "value type implementing interface",
			provide: fx.Provide(
				func() anotherStringer {
					return anotherStringer{
						"another stringer",
					}
				},
				fx.Annotate(
					func(myStringer myStringer) fmt.Stringer {
						return &fromStringer{
							name: myStringer.String(),
						}
					},
					fx.From(new(anotherStringer)),
				),
			),
			invoke: func(s fmt.Stringer) {
				assert.Equal(t, s.String(), "another stringer")
			},
		},
		{
			desc: "provide with multiple types From",
			provide: fx.Provide(
				newFromStringer,
				func() anotherStringer {
					return anotherStringer{
						"another stringer",
					}
				},
				fx.Annotate(
					func(myStringer1 myStringer, myStringer2 myStringer) fmt.Stringer {
						return &fromStringer{
							name: myStringer1.String() + " and " + myStringer2.String(),
						}
					},
					fx.From(new(anotherStringer), new(*fromStringer)),
				),
			),
			invoke: func(s fmt.Stringer) {
				assert.Equal(t, s.String(), "another stringer and a good stringer")
			},
		},
		{
			desc: "provide from with param annotation",
			provide: fx.Provide(
				fx.Annotate(
					newFromStringer,
					fx.ResultTags(`name:"struct1"`),
				),
				fx.Annotate(
					func(myStringer myStringer) fmt.Stringer {
						return &fromStringer{
							name: myStringer.String(),
						}
					},
					fx.From(new(*fromStringer)),
					fx.ParamTags(`name:"struct1"`),
				),
			),
			invoke: func(s fmt.Stringer) {
				assert.Equal(t, s.String(), "a good stringer")
			},
		},
		{
			// same as the test above, except now we annotate
			// it in a different order.
			desc: "provide from with param annotation, in different order",
			provide: fx.Provide(
				fx.Annotate(
					newFromStringer,
					fx.ResultTags(`name:"struct1"`),
				),
				fx.Annotate(
					func(myStringer myStringer) fmt.Stringer {
						return &fromStringer{
							name: myStringer.String(),
						}
					},
					fx.ParamTags(`name:"struct1"`),
					fx.From(new(*fromStringer)),
				),
			),
			invoke: func(s fmt.Stringer) {
				assert.Equal(t, s.String(), "a good stringer")
			},
		},
		{
			desc: "annotate fewer items than required for constructor",
			provide: fx.Provide(
				newFromStringer,
				func() anotherStringer {
					return anotherStringer{
						"another stringer",
					}
				},
				fx.Annotate(
					func(myStringer1 myStringer, fromStringer2 *fromStringer) fmt.Stringer {
						return &fromStringer{
							name: myStringer1.String() + " and " + fromStringer2.String(),
						}
					},
					fx.From(new(anotherStringer)),
				),
			),
			invoke: func(s fmt.Stringer) {
				assert.Equal(t, s.String(), "another stringer and a good stringer")
			},
		},
		{
			desc: "Provide with empty From type",
			provide: fx.Provide(
				newFromStringer,
				fx.Annotate(
					func(myStringer *fromStringer) fmt.Stringer {
						return &fromStringer{
							name: myStringer.String(),
						}
					},
					fx.From(),
				),
			),
			invoke: func(s fmt.Stringer) {
				assert.Equal(t, s.String(), "a good stringer")
			},
		},
		{
			desc: "Provide with variadic function",
			provide: fx.Provide(
				newFromStringer,
				fx.Annotate(
					func(myStringer myStringer, x ...int) fmt.Stringer {
						return &fromStringer{
							name: myStringer.String(),
						}
					},
					fx.From(new(*fromStringer)),
				),
			),
			invoke: func(s fmt.Stringer) {
				assert.Equal(t, s.String(), "a good stringer")
			},
		},
	}

	for _, tt := range tests {
		t.Run(tt.desc, func(t *testing.T) {
			t.Parallel()

			app := NewForTest(t,
				fx.WithLogger(func() fxevent.Logger {
					return fxtest.NewTestLogger(t)
				}),
				tt.provide,
				fx.Invoke(tt.invoke),
			)
			require.NoError(t, app.Err())
		})
	}
}

func TestAnnotatedFromFailures(t *testing.T) {
	t.Parallel()
	type myStringer interface {
		String() string
	}

	newFromStringer := func() *fromStringer {
		return &fromStringer{name: "stringer"}
	}

	tests := []struct {
		desc          string
		provide       fx.Option
		invoke        any
		errorContains string
	}{
		{
			desc: "provide when an illegal type From",
			provide: fx.Provide(
				fx.Annotate(
					func(writer io.Writer) fmt.Stringer {
						return &fromStringer{}
					},
					fx.From(new(*fromStringer)),
				),
			),
			invoke: func(stringer fmt.Stringer) {
				fmt.Println(stringer.String())
			},
			errorContains: "*fx_test.fromStringer does not implement io.Writer",
		},
		{
			desc: "provide with variadic function and an illegal type From",
			provide: fx.Provide(
				fx.Annotate(
					func(writer io.Writer, x ...int) fmt.Stringer {
						return &fromStringer{}
					},
					fx.From(new(*fromStringer)),
				),
			),
			invoke: func(stringer fmt.Stringer) {
				fmt.Println(stringer.String())
			},
			errorContains: "*fx_test.fromStringer does not implement io.Writer",
		},
		{
			desc: "don't provide original type using From",
			provide: fx.Provide(
				fx.Annotate(
					func(myStringer myStringer) fmt.Stringer {
						return &fromStringer{
							name: myStringer.String(),
						}
					},
					fx.From(new(*fromStringer)),
				),
			),
			invoke: func(stringer fmt.Stringer) {
				fmt.Println(stringer.String())
			},
			errorContains: "missing type: *fx_test.fromStringer",
		},
		{
			desc: "fail to provide with name annotation",
			provide: fx.Provide(
				fx.Annotate(
					newFromStringer,
				),
				fx.Annotate(
					func(myStringer myStringer) fmt.Stringer {
						return &fromStringer{
							name: myStringer.String(),
						}
					},
					fx.From(new(*fromStringer)),
					fx.ParamTags(`name:"struct1"`),
				),
			),
			invoke: func(s fmt.Stringer) {
				assert.Equal(t, s.String(), "a good stringer")
			},
			errorContains: `missing type: *fx_test.fromStringer[name="struct1"]`,
		},
		{
			desc: "non-pointer argument to From",
			provide: fx.Provide(
				fx.Annotate(
					newFromStringer,
					fx.From("foo"),
				),
			),
			errorContains: "argument must be a pointer",
		},
		{
			desc: "multiple from annotations",
			provide: fx.Provide(
				fx.Annotate(
					newFromStringer,
					fx.From(new(asStringer)),
					fx.From(new(asStringer)),
				),
			),
			errorContains: "cannot apply more than one line of From",
		},
		{
			desc: "variadic argument",
			provide: fx.Provide(
				fx.Annotate(
					func(ss ...myStringer) {},
					fx.From(new(asStringer)),
				),
			),
			errorContains: "cannot annotate a variadic argument",
		},
	}

	for _, tt := range tests {
		t.Run(tt.desc, func(t *testing.T) {
			t.Parallel()
			app := NewForTest(t,
				fx.WithLogger(func() fxevent.Logger {
					return fxtest.NewTestLogger(t)
				}),
				tt.provide,
				fx.Invoke(tt.invoke),
			)
			err := app.Err()
			require.Error(t, err)
			assert.Contains(t, err.Error(), tt.errorContains)
		})
	}
}

func TestAnnotatedAs(t *testing.T) {
	t.Parallel()
	type in struct {
		fx.In

		S fmt.Stringer `name:"goodStringer"`
	}
	type inSelf struct {
		fx.In

		S1 fmt.Stringer `name:"goodStringer"`
		S2 *asStringer  `name:"goodStringer"`
	}
	type myStringer interface {
		String() string
	}

	newAsStringer := func() *asStringer {
		return &asStringer{
			name: "a good stringer",
		}
	}

	tests := []struct {
		desc     string
		provide  fx.Option
		invoke   any
		startApp bool
	}{
		{
			desc: "provide a good stringer",
			provide: fx.Provide(
				fx.Annotate(newAsStringer, fx.As(new(fmt.Stringer))),
			),
			invoke: func(s fmt.Stringer) {
				assert.Equal(t, s.String(), "a good stringer")
			},
		},
		{
			desc: "value type implementing interface",
			provide: fx.Provide(
				fx.Annotate(func() anotherStringer {
					return anotherStringer{
						"another stringer",
					}
				}, fx.As(new(fmt.Stringer))),
			),
			invoke: func(s fmt.Stringer) {
				assert.Equal(t, s.String(), "another stringer")
			},
		},
		{
			desc: "provide with multiple types As",
			provide: fx.Provide(fx.Annotate(func() (*asStringer, *bytes.Buffer) {
				buf := make([]byte, 1)
				b := bytes.NewBuffer(buf)
				return &asStringer{name: "stringer"}, b
			}, fx.As(new(fmt.Stringer), new(io.Writer)))),
			invoke: func(s fmt.Stringer, w io.Writer) {
				w.Write([]byte(s.String()))
			},
		},
		{
			desc: "provide as with result annotation",
			provide: fx.Provide(
				fx.Annotate(func() *asStringer {
					return &asStringer{name: "stringer"}
				},
					fx.ResultTags(`name:"goodStringer"`),
					fx.As(new(fmt.Stringer))),
			),
			invoke: func(i in) {
				assert.Equal(t, "stringer", i.S.String())
			},
		},
		{
			// same as the test above, except now we annotate
			// it in a different order.
			desc: "provide as with result annotation, in different order",
			provide: fx.Provide(
				fx.Annotate(func() *asStringer {
					return &asStringer{name: "stringer"}
				},
					fx.As(new(fmt.Stringer)),
					fx.ResultTags(`name:"goodStringer"`)),
			),
			invoke: func(i in) {
				assert.Equal(t, "stringer", i.S.String())
			},
		},
		{
			desc: "provide as with result annotation with error",
			provide: fx.Provide(
				fx.Annotate(func() (*asStringer, error) {
					return &asStringer{name: "stringer"}, nil
				},
					fx.ResultTags(`name:"goodStringer"`),
					fx.As(new(fmt.Stringer))),
			),
			invoke: func(i in) {
				assert.Equal(t, "stringer", i.S.String())
			},
		},
		{
			desc: "provide as with result annotation in different order with error",
			provide: fx.Provide(
				fx.Annotate(func() (*asStringer, error) {
					return &asStringer{name: "stringer"}, nil
				},
					fx.As(new(fmt.Stringer)),
					fx.ResultTags(`name:"goodStringer"`)),
			),
			invoke: func(i in) {
				assert.Equal(t, "stringer", i.S.String())
			},
		},
		{
			desc: "provide multiple constructors annotated As",
			provide: fx.Provide(
				fx.Annotate(func() *asStringer {
					return &asStringer{name: "stringer"}
				}, fx.As(new(fmt.Stringer))),
				fx.Annotate(func() *bytes.Buffer {
					buf := make([]byte, 1)
					return bytes.NewBuffer(buf)
				}, fx.As(new(io.Writer))),
			),
			invoke: func(s fmt.Stringer, w io.Writer) {
				assert.Equal(t, "stringer", s.String())
				_, err := w.Write([]byte{1})
				require.NoError(t, err)
			},
		},
		{
			desc: "provide the same provider as multiple types",
			provide: fx.Provide(
				fx.Annotate(newAsStringer, fx.As(new(fmt.Stringer))),
				fx.Annotate(newAsStringer, fx.As(new(myStringer))),
			),
			invoke: func(s fmt.Stringer, ms myStringer) {
				assert.Equal(t, "a good stringer", s.String())
				assert.Equal(t, "a good stringer", ms.String())
			},
		},
		{
			desc: "annotate fx.Supply",
			provide: fx.Supply(
				fx.Annotate(&asStringer{"foo"}, fx.As(new(fmt.Stringer))),
			),
			invoke: func(s fmt.Stringer) {
				assert.Equal(t, "foo", s.String())
			},
		},
		{
			desc: "annotate as many interfaces",
			provide: fx.Provide(
				fx.Annotate(func() (*asStringer, error) {
					return &asStringer{name: "stringer"}, nil
				},
					fx.As(new(fmt.Stringer)),
					fx.As(new(myStringer)),
					fx.ResultTags(`name:"stringer"`)),
			),
			invoke: fx.Annotate(
				func(
					S fmt.Stringer,
					MS myStringer,
				) {
					assert.Equal(t, "stringer", S.String())
					assert.Equal(t, "stringer", MS.String())
				}, fx.ParamTags(`name:"stringer"`, `name:"stringer"`),
			),
		},
		{
			desc: "annotate as many interfaces with both-annotated return values",
			provide: fx.Provide(
				fx.Annotate(func() (*asStringer, *bytes.Buffer) {
					return &asStringer{name: "stringer"},
						bytes.NewBuffer(make([]byte, 1))
				},
					fx.As(new(fmt.Stringer), new(io.Reader)),
					fx.As(new(myStringer), new(io.Writer))),
			),
			invoke: func(s fmt.Stringer, ms myStringer, r io.Reader, w io.Writer) {
				assert.Equal(t, "stringer", s.String())
				assert.Equal(t, "stringer", ms.String())
				_, err := w.Write([]byte("."))
				assert.NoError(t, err)
				buf := make([]byte, 1)
				_, err = r.Read(buf)
				assert.NoError(t, err)
			},
		},
		{
			desc: "annotate as many interfaces with different numbers of annotations",
			provide: fx.Provide(
				fx.Annotate(func() (*asStringer, *bytes.Buffer) {
					return &asStringer{name: "stringer"},
						bytes.NewBuffer(make([]byte, 1))
				},
					// annotate both in here
					fx.As(new(fmt.Stringer), new(io.Writer)),
					// annotate just myStringer here
					fx.As(new(myStringer))),
			),
			invoke: func(s fmt.Stringer, ms myStringer, w io.Writer) {
				assert.Equal(t, "stringer", s.String())
				assert.Equal(t, "stringer", ms.String())
				_, err := w.Write([]byte("."))
				assert.NoError(t, err)
			},
		},
		{
			desc: "annotate many interfaces with varying annotation count and check original type",
			provide: fx.Provide(
				fx.Annotate(func() (*asStringer, *bytes.Buffer) {
					return &asStringer{name: "stringer"},
						bytes.NewBuffer(make([]byte, 1))
				},
					// annotate just myStringer here
					fx.As(new(myStringer)),
					// annotate both in here
					fx.As(new(fmt.Stringer), new(io.Writer))),
			),
			invoke: func(s fmt.Stringer, ms myStringer, buf *bytes.Buffer, w io.Writer) {
				assert.Equal(t, "stringer", s.String())
				assert.Equal(t, "stringer", ms.String())
				_, err := w.Write([]byte("."))
				assert.NoError(t, err)
				_, err = buf.Write([]byte("."))
				assert.NoError(t, err)
			},
		},
		{
			desc: "annotate fewer items than provided constructor",
			provide: fx.Provide(
				fx.Annotate(func() (*bytes.Buffer, *strings.Builder) {
					s := "Hello"
					return bytes.NewBuffer([]byte(s)), &strings.Builder{}
				},
					fx.As(new(io.Reader))),
			),
			invoke: func(r io.Reader) {
			},
		},
		{
			desc: "results annotated as are provided to hooks as annotated types",
			provide: fx.Provide(
				fx.Annotate(func() (*asStringer, *bytes.Buffer) {
					return &asStringer{name: "stringer"},
						bytes.NewBuffer([]byte{})
				},
					// lifecycle hook added is able to receive results as annotated
					fx.OnStart(func(s fmt.Stringer, ms myStringer, buf *bytes.Buffer, w io.Writer) {
						assert.Equal(t, "stringer", s.String())
						assert.Equal(t, "stringer", ms.String())
						_, err := w.Write([]byte("."))
						assert.NoError(t, err)
						_, err = buf.Write([]byte("."))
						assert.NoError(t, err)
					}),
					fx.OnStop(func(buf *bytes.Buffer) {
						assert.Equal(t, "....", buf.String(), "buffer should contain bytes written in Invoke func and OnStart hook")
					}),
					// annotate just myStringer here
					fx.As(new(myStringer)),
					// annotate both in here
					fx.As(new(fmt.Stringer), new(io.Writer))),
			),
			invoke: func(s fmt.Stringer, ms myStringer, buf *bytes.Buffer, w io.Writer) {
				assert.Equal(t, "stringer", s.String())
				assert.Equal(t, "stringer", ms.String())
				_, err := w.Write([]byte("."))
				assert.NoError(t, err)
				_, err = buf.Write([]byte("."))
				assert.NoError(t, err)
			},
			startApp: true,
		},
		{
			desc: "self w other As annotations",
			provide: fx.Provide(
				fx.Annotate(
					func() *asStringer {
						return &asStringer{name: "stringer"}
					},
					fx.As(fx.Self()),
					fx.As(new(fmt.Stringer)),
				),
			),
			invoke: func(s fmt.Stringer, as *asStringer) {
				assert.Equal(t, "stringer", s.String())
				assert.Equal(t, "stringer", as.String())
			},
		},
		{
			desc: "self as one As target",
			provide: fx.Provide(
				fx.Annotate(
					func() (*asStringer, *bytes.Buffer) {
						s := &asStringer{name: "stringer"}
						b := &bytes.Buffer{}
						return s, b
					},
					fx.As(fx.Self(), new(io.Writer)),
				),
			),
			invoke: func(s *asStringer, w io.Writer) {
				assert.Equal(t, "stringer", s.String())
				_, err := w.Write([]byte("."))
				assert.NoError(t, err)
			},
		},
		{
			desc: "two as, two self, four types",
			provide: fx.Provide(
				fx.Annotate(
					func() (*asStringer, *bytes.Buffer) {
						s := &asStringer{name: "stringer"}
						b := &bytes.Buffer{}
						return s, b
					},
					fx.As(fx.Self(), new(io.Writer)),
					fx.As(new(fmt.Stringer)),
				),
			),
			invoke: func(s1 *asStringer, s2 fmt.Stringer, b *bytes.Buffer, w io.Writer) {
				assert.Equal(t, "stringer", s1.String())
				assert.Equal(t, "stringer", s2.String())
				_, err := w.Write([]byte("."))
				assert.NoError(t, err)
				_, err = b.Write([]byte("."))
				assert.NoError(t, err)
			},
		},
		{
			desc: "self with lifecycle hook",
			provide: fx.Provide(
				fx.Annotate(
					func() *asStringer {
						return &asStringer{name: "stringer"}
					},
					fx.As(fx.Self()),
					fx.As(new(fmt.Stringer)),
					fx.OnStart(func(s fmt.Stringer, as *asStringer) {
						assert.Equal(t, "stringer", s.String())
						assert.Equal(t, "stringer", as.String())
					}),
				),
			),
			invoke: func(s fmt.Stringer, as *asStringer) {
				assert.Equal(t, "stringer", s.String())
				assert.Equal(t, "stringer", as.String())
			},
			startApp: true,
		},
		{
			desc: "self with result tags",
			provide: fx.Provide(
				fx.Annotate(
					func() *asStringer {
						return &asStringer{name: "stringer"}
					},
					fx.As(fx.Self()),
					fx.As(new(fmt.Stringer)),
					fx.ResultTags(`name:"goodStringer"`),
				),
			),
			invoke: func(i inSelf) {
				assert.Equal(t, "stringer", i.S1.String())
				assert.Equal(t, "stringer", i.S2.String())
			},
		},
	}

	for _, tt := range tests {
		t.Run(tt.desc, func(t *testing.T) {
			t.Parallel()

			app := NewForTest(t,
				fx.WithLogger(func() fxevent.Logger {
					return fxtest.NewTestLogger(t)
				}),
				tt.provide,
				fx.Invoke(tt.invoke),
			)
			require.NoError(t, app.Err())
			if tt.startApp {
				ctx := context.Background()
				require.NoError(t, app.Start(ctx))
				require.NoError(t, app.Stop(ctx))
			}
		})
	}
}

func TestAnnotatedAsFailures(t *testing.T) {
	t.Parallel()

	newAsStringer := func() *asStringer {
		return &asStringer{name: "stringer"}
	}

	newAsStringerWithErr := func() (*asStringer, error) {
		return nil, errors.New("great sadness")
	}

	tests := []struct {
		desc          string
		provide       fx.Option
		invoke        any
		errorContains string
	}{
		{
			desc:          "provide when an illegal type As",
			provide:       fx.Provide(fx.Annotate(newAsStringer, fx.As(new(io.Writer)))),
			invoke:        func() {},
			errorContains: "asStringer does not implement io.Writer",
		},
		{
			desc:          "provide when an illegal type As with result tag",
			provide:       fx.Provide(fx.Annotate(newAsStringer, fx.ResultTags(`name:"stringer"`), fx.As(new(io.Writer)))),
			invoke:        func() {},
			errorContains: "asStringer does not implement io.Writer",
		},
		{
			desc:          "error is propagated without result tag",
			provide:       fx.Provide(fx.Annotate(newAsStringerWithErr, fx.As(new(fmt.Stringer)))),
			invoke:        func(_ fmt.Stringer) {},
			errorContains: "great sadness",
		},
		{
			desc:          "error is propagated with result tag",
			provide:       fx.Provide(fx.Annotate(newAsStringerWithErr, fx.ResultTags(`name:"stringer"`), fx.As(new(fmt.Stringer)))),
			invoke:        fx.Annotate(func(_ fmt.Stringer) {}, fx.ParamTags(`name:"stringer"`)),
			errorContains: "great sadness",
		},
		{
			desc:    "don't provide original type using As",
			provide: fx.Provide(fx.Annotate(newAsStringer, fx.As(new(fmt.Stringer)))),
			invoke: func(as *asStringer) {
				fmt.Println(as.String())
			},
			errorContains: "missing type: *fx_test.asStringer",
		},
		{
			desc: "fail to provide with name annotation",
			provide: fx.Provide(fx.Annotate(func(n string) *asStringer {
				return &asStringer{name: n}
			}, fx.As(new(fmt.Stringer)), fx.ParamTags(`name:"n"`))),
			invoke: func(a fmt.Stringer) {
				fmt.Println(a)
			},
			errorContains: `missing type: string[name="n"]`,
		},
		{
			desc: "non-pointer argument to As",
			provide: fx.Provide(
				fx.Annotate(
					newAsStringer,
					fx.As("foo"),
				),
			),
			errorContains: "argument must be a pointer to an interface: got string",
		},
	}

	for _, tt := range tests {
		t.Run(tt.desc, func(t *testing.T) {
			t.Parallel()
			app := NewForTest(t,
				fx.WithLogger(func() fxevent.Logger {
					return fxtest.NewTestLogger(t)
				}),
				tt.provide,
				fx.Invoke(tt.invoke),
			)
			err := app.Err()
			require.Error(t, err)
			assert.Contains(t, err.Error(), tt.errorContains)
		})
	}
}

func TestAnnotatedWrongUsage(t *testing.T) {
	t.Parallel()

	type a struct {
		name string
	}
	type in struct {
		fx.In

		A *a `name:"foo"`
	}
	newA := func() *a {
		return &a{name: "foo"}
	}

	t.Run("In Constructor", func(t *testing.T) {
		t.Parallel()

		var in in
		app := NewForTest(t,
			fx.WithLogger(func() fxevent.Logger {
				return fxtest.NewTestLogger(t)
			}),
			fx.Provide(
				func() fx.Annotated {
					return fx.Annotated{
						Name:   "foo",
						Target: newA,
					}
				},
			),
			fx.Populate(&in),
		)

		err := app.Err()
		require.Error(t, err)

		// Example:
		// 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:
		// go.uber.org/fx_test.TestAnnotatedWrongUsage.func2
		//         /.../fx/annotated_test.go:76
		// testing.tRunner
		//         /.../go/1.13.3/libexec/src/testing/testing.go:909
		assert.Contains(t, err.Error(), "fx.Annotated should be passed to fx.Provide directly, it should not be returned by the constructor")
		assert.Contains(t, err.Error(), "fx.Provide received go.uber.org/fx_test.TestAnnotatedWrongUsage")
		assert.Contains(t, err.Error(), "go.uber.org/fx_test.TestAnnotatedWrongUsage")
		assert.Contains(t, err.Error(), "/annotated_test.go")
	})

	t.Run("Result Type", func(t *testing.T) {
		t.Parallel()

		app := NewForTest(t,
			fx.WithLogger(func() fxevent.Logger {
				return fxtest.NewTestLogger(t)
			}),
			fx.Provide(
				fx.Annotated{
					Name: "foo",
					Target: func() in {
						return in{A: &a{name: "foo"}}
					},
				},
			),
		)
		assert.Contains(t, app.Err().Error(), "embeds a dig.In", "expected error when result types were annotated")
	})

	t.Run("invalid group option", func(t *testing.T) {
		t.Parallel()

		app := NewForTest(t,
			fx.Provide(
				fx.Annotate(func() string { return "sad times" },
					fx.ResultTags(`group:"foo,soft"`)),
			),
		)
		assert.Contains(t, app.Err().Error(), "cannot use soft with result value groups", "expected error when invalid group option is provided")
	})
}

func TestAnnotatedString(t *testing.T) {
	t.Parallel()

	tests := []struct {
		desc string
		give fx.Annotated
		want string
	}{
		{
			desc: "empty",
			give: fx.Annotated{},
			want: "fx.Annotated{}",
		},
		{
			desc: "name",
			give: fx.Annotated{Name: "foo"},
			want: `fx.Annotated{Name: "foo"}`,
		},
		{
			desc: "group",
			give: fx.Annotated{Group: "foo"},
			want: `fx.Annotated{Group: "foo"}`,
		},
		{
			desc: "name and group",
			give: fx.Annotated{Name: "foo", Group: "bar"},
			want: `fx.Annotated{Name: "foo", Group: "bar"}`,
		},
		{
			desc: "target",
			give: fx.Annotated{Target: func() {}},
			want: "fx.Annotated{Target: go.uber.org/fx_test.TestAnnotatedString.func1()}",
		},
		{
			desc: "name and target",
			give: fx.Annotated{Name: "foo", Target: func() {}},
			want: `fx.Annotated{Name: "foo", Target: go.uber.org/fx_test.TestAnnotatedString.func2()}`,
		},
		{
			desc: "group and target",
			give: fx.Annotated{Group: "foo", Target: func() {}},
			want: `fx.Annotated{Group: "foo", Target: go.uber.org/fx_test.TestAnnotatedString.func3()}`,
		},
		{
			desc: "name, group and target",
			give: fx.Annotated{Name: "foo", Group: "bar", Target: func() {}},
			want: `fx.Annotated{Name: "foo", Group: "bar", Target: go.uber.org/fx_test.TestAnnotatedString.func4()}`,
		},
	}

	for _, tt := range tests {
		t.Run(tt.desc, func(t *testing.T) {
			t.Parallel()

			assert.Equal(t, tt.want, tt.give.String())
		})
	}
}

func TestAnnotate(t *testing.T) {
	t.Parallel()

	type a struct{}
	type b struct{ a *a }
	type c struct{ b *b }
	type sliceA struct{ sa []*a }
	newA := func() *a { return &a{} }
	newB := func(a *a) *b {
		return &b{a}
	}
	newC := func(b *b) *c {
		return &c{b}
	}
	newSliceA := func(sa ...*a) *sliceA {
		return &sliceA{sa}
	}
	newSliceAWithB := func(b *b, sa ...*a) *sliceA {
		total := append(sa, b.a)
		return &sliceA{total}
	}

	t.Run("Provide with empty param+result tags", func(t *testing.T) {
		t.Parallel()

		app := fxtest.New(t,
			fx.Provide(
				newA,
				fx.Annotate(newB, fx.ParamTags(), fx.ResultTags()),
			),
			fx.Invoke(newC),
		)
		defer app.RequireStart().RequireStop()
		require.NoError(t, app.Err())
	})

	t.Run("Provide with optional", func(t *testing.T) {
		t.Parallel()

		app := fxtest.New(t,
			fx.Provide(
				fx.Annotate(newB, fx.ParamTags(`name:"a" optional:"true"`)),
			),
			fx.Invoke(newC),
		)
		defer app.RequireStart().RequireStop()
		require.NoError(t, app.Err())
	})

	t.Run("Provide with many annotated params", func(t *testing.T) {
		t.Parallel()

		app := fxtest.New(t,
			fx.Provide(
				fx.Annotate(newB, fx.ParamTags(`optional:"true"`)),
				fx.Annotate(func(a *a, b *b) any { return nil },
					fx.ParamTags(`name:"a" optional:"true"`, `name:"b"`),
					fx.ResultTags(`name:"nil"`),
				),
			),
			fx.Invoke(newC),
		)
		defer app.RequireStart().RequireStop()
		require.NoError(t, app.Err())
	})

	t.Run("Invoke with optional", func(t *testing.T) {
		t.Parallel()

		app := NewForTest(t,
			fx.Invoke(
				fx.Annotate(newB, fx.ParamTags(`optional:"true"`)),
			),
		)
		err := app.Err()
		require.NoError(t, err)
	})

	t.Run("Invoke with a missing dependency", func(t *testing.T) {
		t.Parallel()

		app := NewForTest(t,
			fx.Invoke(
				fx.Annotate(newB, fx.ParamTags(`name:"a"`)),
			),
		)
		err := app.Err()
		require.Error(t, err)
		assert.Contains(t, err.Error(), `missing dependencies`)
		assert.Contains(t, err.Error(), `missing type: *fx_test.a[name="a"]`)
	})

	t.Run("Provide with variadic function", func(t *testing.T) {
		t.Parallel()

		var got *sliceA
		app := fxtest.New(t,
			fx.Provide(
				fx.Annotated{Group: "as", Target: newA},
				fx.Annotated{Group: "as", Target: newA},
				fx.Annotate(newSliceA,
					fx.ParamTags(`group:"as"`),
				),
			),
			fx.Populate(&got),
		)
		defer app.RequireStart().RequireStop()
		require.NoError(t, app.Err())

		assert.Len(t, got.sa, 2)
	})

	t.Run("Provide variadic function with no optional params", func(t *testing.T) {
		t.Parallel()

		var got struct {
			fx.In

			Result *sliceA `name:"as"`
		}
		app := fxtest.New(t,
			fx.Supply([]*a{{}, {}, {}}),
			fx.Provide(
				fx.Annotate(newSliceA,
					fx.ResultTags(`name:"as"`),
				),
			),
			fx.Populate(&got),
		)
		defer app.RequireStart().RequireStop()
		require.NoError(t, app.Err())
		assert.Len(t, got.Result.sa, 3)
	})

	t.Run("Provide variadic function named with no given params", func(t *testing.T) {
		t.Parallel()

		var got *sliceA
		app := NewForTest(t,
			fx.Provide(
				fx.Annotate(newSliceA, fx.ParamTags(`name:"a"`)),
			),
			fx.Populate(&got),
		)
		err := app.Err()
		require.Error(t, err)
		assert.Contains(t, err.Error(), `missing dependencies`)
		assert.Contains(t, err.Error(), `missing type: []*fx_test.a[name="a"]`)
	})

	t.Run("Invoke function with soft group param", func(t *testing.T) {
		t.Parallel()
		newF := func(foos []int, bar string) {
			assert.ElementsMatch(t, []int{10}, foos)
		}
		app := fxtest.New(t,
			fx.Provide(
				fx.Annotate(
					func() (int, string) { return 10, "hello" },
					fx.ResultTags(`group:"foos"`),
				),
				fx.Annotate(
					func() int {
						require.FailNow(t, "this function should not be called")
						return 20
					},
					fx.ResultTags(`group:"foos"`),
				),
			),
			fx.Invoke(
				fx.Annotate(newF, fx.ParamTags(`group:"foos,soft"`)),
			),
		)

		defer app.RequireStart().RequireStop()
		require.NoError(t, app.Err())
	})

	t.Run("Invoke variadic function with multiple params", func(t *testing.T) {
		t.Parallel()

		app := fxtest.New(t,
			fx.Supply(
				fx.Annotate(newB(newA()), fx.ResultTags(`name:"b"`)),
			),
			fx.Invoke(fx.Annotate(newSliceAWithB, fx.ParamTags(`name:"b"`))),
		)

		defer app.RequireStart().RequireStop()
		require.NoError(t, app.Err())
	})

	t.Run("Invoke non-optional variadic function with a missing dependency", func(t *testing.T) {
		t.Parallel()

		app := NewForTest(t,
			fx.Invoke(
				fx.Annotate(newSliceA, fx.ParamTags(`optional:"false"`)),
			),
		)
		err := app.Err()
		require.Error(t, err)
		assert.Contains(t, err.Error(), `missing dependencies`)
		assert.Contains(t, err.Error(), `missing type: []*fx_test.a`)
	})

	t.Run("Invoke with variadic function", func(t *testing.T) {
		t.Parallel()

		type T1 struct{ s string }

		app := fxtest.New(t,
			fx.Supply(
				fx.Annotate(T1{"foo"}, fx.ResultTags(`group:"t"`)),
				fx.Annotate(T1{"bar"}, fx.ResultTags(`group:"t"`)),
				fx.Annotate(T1{"baz"}, fx.ResultTags(`group:"t"`)),
			),
			fx.Invoke(fx.Annotate(func(got ...T1) {
				assert.ElementsMatch(t, []T1{{"foo"}, {"bar"}, {"baz"}}, got)
			}, fx.ParamTags(`group:"t"`))),
		)

		defer app.RequireStart().RequireStop()
		require.NoError(t, app.Err())
	})

	t.Run("provide with annotated results", func(t *testing.T) {
		t.Parallel()

		app := fxtest.New(t,
			fx.Provide(
				fx.Annotate(func() *a {
					return &a{}
				}, fx.ResultTags(`name:"firstA"`)),
				fx.Annotate(func() *a {
					return &a{}
				}, fx.ResultTags(`name:"secondA"`)),
				fx.Annotate(func() *a {
					return &a{}
				}, fx.ResultTags(`name:"thirdA"`)),
				fx.Annotate(func(a1 *a, a2 *a, a3 *a) *b {
					return &b{a1}
				}, fx.ParamTags(
					`name:"firstA"`,
					`name:"secondA"`,
					`name:"thirdA"`,
				)),
			),
			fx.Invoke(newC),
		)

		require.NoError(t, app.Err())
		defer app.RequireStart().RequireStop()
	})

	t.Run("provide with missing annotated results", func(t *testing.T) {
		t.Parallel()

		app := NewForTest(t,
			fx.Provide(
				fx.Annotate(func() *a {
					return &a{}
				}, fx.ResultTags(`name:"firstA"`)),
				fx.Annotate(func() *a {
					return &a{}
				}, fx.ResultTags(`name:"secondA"`)),
				fx.Annotate(func() *a {
					return &a{}
				}, fx.ResultTags(`name:"fourthA"`)),
			),
			fx.Invoke(
				fx.Annotate(func(a1 *a, a2 *a, a3 *a) *b {
					return &b{a1}
				}, fx.ParamTags(
					`name:"firstA"`,
					`name:"secondA"`,
					`name:"thirdA"`)),
			),
		)
		err := app.Err()
		require.Error(t, err)
		assert.Contains(t, err.Error(), `missing type: *fx_test.a[name="thirdA"]`)
	})

	t.Run("error in the middle of a function", func(t *testing.T) {
		t.Parallel()

		app := NewForTest(t,
			fx.Provide(
				fx.Annotate(func() (*a, error, *a) { //nolint:staticcheck // we want to test error in the middle.
					return &a{}, nil, &a{}
				}, fx.ResultTags(`name:"firstA"`, ``, `name:"secondA"`)),
			),
			fx.Invoke(
				fx.Annotate(func(*a) {}, fx.ParamTags(`name:"firstA"`)),
			),
		)
		err := app.Err()
		require.Error(t, err)
		assert.Contains(t, err.Error(), "only the last result can be an error")
		assert.Contains(t, err.Error(), "returns error as result 1")
	})

	t.Run("provide with annotated results with error", func(t *testing.T) {
		t.Parallel()

		app := fxtest.New(t,
			fx.Provide(
				fx.Annotate(func() (*a, *a, error) {
					return &a{}, &a{}, nil
				}, fx.ResultTags(`name:"firstA"`, `name:"secondA"`)),
				fx.Annotate(func() (*a, error) {
					return &a{}, nil
				}, fx.ResultTags(`name:"thirdA"`)),
			),
			fx.Invoke(fx.Annotate(func(a1 *a, a2 *a, a3 *a) *b {
				return &b{a2}
			}, fx.ParamTags(`name:"firstA"`, `name:"secondA"`, `name:"thirdA"`))))

		require.NoError(t, app.Err())
		defer app.RequireStart().RequireStop()
	})

	t.Run("provide an already provided function using Annotate", func(t *testing.T) {
		t.Parallel()

		app := NewForTest(t,
			fx.Provide(fx.Annotate(newA, fx.ResultTags(`name:"a"`))),
			fx.Provide(fx.Annotate(newA, fx.ResultTags(`name:"a"`))),
			fx.Invoke(
				fx.Annotate(newB, fx.ParamTags(`name:"a"`)),
			),
		)
		err := app.Err()
		require.Error(t, err)
		assert.Contains(t, err.Error(), "already provided")
		assert.Contains(t, err.Error(), "go.uber.org/fx_test.TestAnnotate.func")
	})

	t.Run("specify more ParamTags than Params", func(t *testing.T) {
		t.Parallel()

		app := fxtest.New(t,
			fx.Provide(
				// This should just leave newA as it is.
				fx.Annotate(newA, fx.ParamTags(`name:"something"`)),
			),
			fx.Invoke(newB),
		)

		err := app.Err()
		require.NoError(t, err)
		defer app.RequireStart().RequireStop()
	})

	t.Run("specify two ParamTags", func(t *testing.T) {
		t.Parallel()

		app := NewForTest(t,
			fx.Provide(
				// This should just leave newA as it is.
				fx.Annotate(
					newA,
					fx.ParamTags(`name:"something"`),
					fx.ParamTags(`name:"anotherThing"`),
				),
			),
			fx.Invoke(newB),
		)
		err := app.Err()
		require.Error(t, err)
		assert.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")
	})

	t.Run("specify two ResultTags", func(t *testing.T) {
		t.Parallel()

		app := NewForTest(t,
			fx.Provide(
				// This should just leave newA as it is.
				fx.Annotate(
					newA,
					fx.ResultTags(`name:"A"`),
					fx.ResultTags(`name:"AA"`),
				),
			),
			fx.Invoke(newB),
		)

		err := app.Err()
		require.Error(t, err)
		assert.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")
	})

	t.Run("annotate with a non-nil error", func(t *testing.T) {
		t.Parallel()

		app := NewForTest(t,
			fx.Provide(
				fx.Annotate(func() (*bytes.Buffer, error) {
					buf := make([]byte, 1)
					return bytes.NewBuffer(buf), errors.New("some error")
				}, fx.ResultTags(`name:"buf"`))),
			fx.Invoke(
				fx.Annotate(func(b *bytes.Buffer) {
					b.Write([]byte{1})
				}, fx.ParamTags(`name:"buf"`))),
		)
		err := app.Err()
		require.Error(t, err)
		assert.Contains(t, err.Error(), "some error")
	})

	t.Run("annotate with a non-nil error and nil error", func(t *testing.T) {
		t.Parallel()

		app := NewForTest(t,
			fx.Provide(
				fx.Annotate(func() (*bytes.Buffer, error) {
					buf := make([]byte, 1)
					return bytes.NewBuffer(buf), errors.New("some error")
				}, fx.ResultTags(`name:"buf1"`)),
				fx.Annotate(func() (*bytes.Buffer, error) {
					buf := make([]byte, 1)
					return bytes.NewBuffer(buf), nil
				}, fx.ResultTags(`name:"buf2"`))),
			fx.Invoke(
				fx.Annotate(func(b1 *bytes.Buffer, b2 *bytes.Buffer) {
					b1.Write([]byte{1})
					b2.Write([]byte{1})
				}, fx.ParamTags(`name:"buf1"`, `name:"buf2"`))),
		)
		err := app.Err()
		require.Error(t, err)
		assert.Contains(t, err.Error(), "some error")
	})

	t.Run("provide annotated non-function", func(t *testing.T) {
		t.Parallel()

		app := NewForTest(t,
			fx.Provide(
				fx.Annotate(42, fx.ResultTags(`name:"buf"`)),
			),
		)
		err := app.Err()
		require.Error(t, err)

		// Example:
		// fx.Provide(fx.Annotate(42, fx.ResultTags(["name:\"buf\""])) from:
		// go.uber.org/fx_test.TestAnnotate.func17
		//     /Users/abg/dev/fx/annotated_test.go:697
		// testing.tRunner
		//     /usr/local/Cellar/go/1.17.2/libexec/src/testing/testing.go:1259
		// Failed: must provide constructor function, got 42 (int)

		assert.Contains(t, err.Error(), "fx.Provide(fx.Annotate(42")
		assert.Contains(t, err.Error(), "must provide constructor function, got 42 (int)")
	})

	t.Run("invoke annotated non-function", func(t *testing.T) {
		t.Parallel()

		app := NewForTest(t,
			fx.Invoke(
				fx.Annotate(42, fx.ParamTags(`name:"buf"`)),
			),
		)
		err := app.Err()
		require.Error(t, err)
		assert.Contains(t, err.Error(), "must provide constructor function, got 42 (int)")
	})

	t.Run("annotate a fx.Out with ResultTags", func(t *testing.T) {
		t.Parallel()

		type A struct {
			s string

			fx.Out
		}

		f := func() A {
			return A{s: "hi"}
		}

		app := NewForTest(t,
			fx.Provide(
				fx.Annotate(f, fx.ResultTags(`name:"out"`)),
			),
		)

		err := app.Err()
		require.Error(t, err)
		assert.Contains(t, err.Error(), "fx.Out structs cannot be annotated with fx.ResultTags or fx.As")
	})

	t.Run("annotate a fx.Out with As", func(t *testing.T) {
		t.Parallel()

		type I any

		type B struct {
			// implements I
		}

		type Res struct {
			fx.Out

			AB B
		}

		f := func() Res {
			return Res{AB: B{}}
		}

		app := NewForTest(t,
			fx.Provide(
				fx.Annotate(f, fx.As(new(I))),
			),
		)

		err := app.Err()
		require.Error(t, err)
		assert.Contains(t, err.Error(), "fx.Out structs cannot be annotated with fx.ResultTags or fx.As")
	})

	t.Run("annotate a fx.In with ParamTags", func(t *testing.T) {
		t.Parallel()

		type A struct {
			S string
		}
		type B struct {
			fx.In
		}

		app := NewForTest(t,
			fx.Provide(
				fx.Annotate(func(i A) string { return i.S }, fx.ParamTags(`optional:"true"`)),
				fx.Annotate(func(i B) string { return "ok" }, fx.ParamTags(`name:"problem"`)),
			),
		)
		err := app.Err()
		require.Error(t, err)
		assert.NotContains(t, err.Error(), "invalid annotation function func(fx_test.A) string")
		assert.Contains(t, err.Error(), "invalid annotation function func(fx_test.B) string")
		assert.Contains(t, err.Error(), "fx.In structs cannot be annotated with fx.ParamTags or fx.From")
	})

	t.Run("annotate a fx.In with From", func(t *testing.T) {
		t.Parallel()

		type I any

		type B struct {
			// implements I
		}

		type Param struct {
			fx.In
			BInterface I
		}

		app := NewForTest(t,
			fx.Provide(
				fx.Annotate(func(p Param) string { return "ok" }, fx.From(new(B))),
			),
		)
		err := app.Err()
		require.Error(t, err)
		assert.Contains(t, err.Error(), "invalid annotation function func(fx_test.Param) string")
		assert.Contains(t, err.Error(), "fx.In structs cannot be annotated with fx.ParamTags or fx.From")
	})

	t.Run("annotate fx.In with fx.ResultTags", func(t *testing.T) {
		t.Parallel()

		type A struct {
			fx.In

			I int
		}

		app := NewForTest(t,
			fx.Provide(
				fx.Annotate(func(a A) string { return "ok" + strconv.Itoa(a.I) }, fx.ResultTags(`name:"val"`)),
				func() int {
					return 1
				},
			),
			fx.Invoke(
				fx.Annotate(func(s string) {
					assert.Equal(t, "ok1", s)
				}, fx.ParamTags(`name:"val"`)),
			),
		)
		err := app.Err()
		require.NoError(t, err)
	})

	t.Run("annotate fx.Out with fx.ParamTags", func(t *testing.T) {
		t.Parallel()

		type A struct {
			fx.Out

			S string
		}

		app := NewForTest(t,
			fx.Provide(
				fx.Annotate(func() int { return 1 }, fx.ResultTags(`name:"val"`)),
				fx.Annotate(func(i int) A { return A{S: strconv.Itoa(i)} }, fx.ParamTags(`name:"val"`)),
			),
			fx.Invoke(func(s string) {
				assert.Equal(t, "1", s)
			}),
		)
		err := app.Err()
		require.NoError(t, err)
	})
}

func TestAnnotateApplyFail(t *testing.T) {
	type a struct{}
	type b struct{ a *a }
	newA := func() *a { return &a{} }
	newB := func(a *a) *b {
		return &b{a}
	}

	var (
		errTagSyntaxSpace            = `multiple tags are not separated by space`
		errTagKeySyntax              = "tag key is invalid, Use group, name or optional as tag keys"
		errTagValueSyntaxQuote       = `tag value should start with double quote. i.e. key:"value" `
		errTagValueSyntaxEndingQuote = `tag value should end in double quote. i.e. key:"value" `
	)
	tests := []struct {
		give                 string
		wantErr              string
		giveAnnotationParam  fx.Annotation
		giveAnnotationResult fx.Annotation
	}{
		{
			give:                 "Tags value invalid ending quote",
			wantErr:              errTagValueSyntaxEndingQuote,
			giveAnnotationParam:  fx.ParamTags(`name:"something'`),
			giveAnnotationResult: fx.ResultTags(`name:"something'`),
		},
		{
			give:                 "Tags value wrong starting quote",
			wantErr:              errTagValueSyntaxQuote,
			giveAnnotationParam:  fx.ParamTags(`name:"something" optional:'true"`),
			giveAnnotationResult: fx.ResultTags(`name:"something" optional:'true"`),
		},
		{
			give:                 "Tags multiple tags not separated by space",
			wantErr:              errTagSyntaxSpace,
			giveAnnotationParam:  fx.ParamTags(`name:"something"group:"something"`),
			giveAnnotationResult: fx.ResultTags(`name:"something"group:"something"`),
		},
		{
			give:                 "Tags key not equal to group, name or optional",
			wantErr:              errTagKeySyntax,
			giveAnnotationParam:  fx.ParamTags(`name1:"something"`),
			giveAnnotationResult: fx.ResultTags(`name1:"something"`),
		},
		{
			give:                 "Tags key empty",
			wantErr:              errTagKeySyntax,
			giveAnnotationParam:  fx.ParamTags(`:"something"`),
			giveAnnotationResult: fx.ResultTags(`:"something"`),
		},
	}
	for _, tt := range tests {
		t.Run("Param "+tt.give, func(t *testing.T) {
			app := NewForTest(t,
				fx.Provide(
					fx.Annotate(
						newA,
						tt.giveAnnotationParam,
					),
				),
				fx.Invoke(newB),
			)
			assert.ErrorContains(t, app.Err(), tt.wantErr)
		})
		t.Run("Result "+tt.give, func(t *testing.T) {
			app := NewForTest(t,
				fx.Provide(
					fx.Annotate(
						newA,
						tt.giveAnnotationResult,
					),
				),
				fx.Invoke(newB),
			)
			assert.ErrorContains(t, app.Err(), tt.wantErr)
		})
	}
}

func TestAnnotateApplySuccess(t *testing.T) {
	type a struct{}
	type b struct{ a *a }
	newA := func() *a { return &a{} }
	newB := func(a *a) *b {
		return &b{a}
	}

	tests := []struct {
		give                 string
		giveAnnotationParam  fx.Annotation
		giveAnnotationResult fx.Annotation
	}{
		{
			give:                 "ParamTags Tag Empty",
			giveAnnotationParam:  fx.ParamTags(`  `),
			giveAnnotationResult: fx.ResultTags(`  `),
		},
		{
			give:                 "ParamTags Tag Empty with extra spaces",
			giveAnnotationParam:  fx.ParamTags(`name:"versionNum"`, `  `),
			giveAnnotationResult: fx.ResultTags(`   `, `group:"versionNum"`),
		},
		{
			give:                 "ParamTags Tag with \\ ",
			giveAnnotationParam:  fx.ParamTags(`name:"version\\Num"`, `  `),
			giveAnnotationResult: fx.ResultTags(``, `group:"version\\Num"`),
		},
	}
	for _, tt := range tests {
		t.Run(tt.give, func(t *testing.T) {
			app := NewForTest(t,
				fx.Provide(
					fx.Annotate(
						newA,
						tt.giveAnnotationParam,
						tt.giveAnnotationResult,
					),
				),
				fx.Invoke(newB),
			)
			require.NoError(t, app.Err())
		})
	}
}

func assertApp(
	t *testing.T,
	app interface {
		Start(context.Context) error
		Stop(context.Context) error
	},
	started *bool,
	stopped *bool,
	invoked *bool,
) {
	t.Helper()
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()
	assert.False(t, *started)
	require.NoError(t, app.Start(ctx))
	assert.True(t, *started)

	if invoked != nil {
		assert.True(t, *invoked)
	}

	if stopped != nil {
		assert.False(t, *stopped)
		require.NoError(t, app.Stop(ctx))
		assert.True(t, *stopped)
	}

	defer app.Stop(ctx)
}

func TestHookAnnotations(t *testing.T) {
	t.Parallel()

	type a struct{}
	type b struct{ a *a }
	type c struct{ b *b }
	newB := func(a *a) *b {
		return &b{a}
	}
	newC := func(b *b) *c {
		return &c{b}
	}

	t.Run("with hook on invoke", func(t *testing.T) {
		t.Parallel()

		var (
			started bool
			stopped bool
			invoked bool
		)
		hook := fx.Annotate(
			func() {
				invoked = true
			},
			fx.OnStart(func(context.Context) error {
				started = true
				return nil
			}),
			fx.OnStop(func(context.Context) error {
				stopped = true
				return nil
			}),
		)
		app := fxtest.New(t, fx.Invoke(hook))

		assertApp(t, app, &started, &stopped, &invoked)
	})

	t.Run("depend on result interface of target", func(t *testing.T) {
		type stub interface {
			String() string
		}

		var started bool

		hook := fx.Annotate(
			func() (stub, error) {
				b := []byte("expected")
				return bytes.NewBuffer(b), nil
			},
			fx.OnStart(func(_ context.Context, s stub) error {
				started = true
				require.Equal(t, "expected", s.String())
				return nil
			}),
		)

		app := fxtest.New(t,
			fx.Provide(hook),
			fx.Invoke(func(s stub) {
				require.Equal(t, "expected", s.String())
			}),
		)

		assertApp(t, app, &started, nil, nil)
	})

	t.Run("start and stop without dependencies", func(t *testing.T) {
		t.Parallel()

		type stub any

		var (
			invoked bool
			started bool
			stopped bool
		)

		hook := fx.Annotate(
			func() (stub, error) { return nil, nil },
			fx.OnStart(func(context.Context) error {
				started = true
				return nil
			}),
			fx.OnStop(func(context.Context) error {
				stopped = true
				return nil
			}),
		)

		app := fxtest.New(t,
			fx.Provide(hook),
			fx.Invoke(func(s stub) {
				invoked = s == nil
			}),
		)

		assertApp(t, app, &started, &stopped, &invoked)
	})

	t.Run("with multiple extra dependency parameters", func(t *testing.T) {
		t.Parallel()

		type (
			A any
			B any
			C any
		)

		var value int

		hook := fx.Annotate(
			func(b B, c C) (A, error) { return nil, nil },
			fx.OnStart(func(_ context.Context, b B, c C) error {
				b1, _ := b.(int)
				c1, _ := c.(int)
				value = b1 + c1
				return nil
			}),
		)

		app := fxtest.New(t,
			fx.Provide(hook),
			fx.Provide(func() B { return int(1) }),
			fx.Provide(func() C { return int(2) }),
			fx.Invoke(func(A) {}),
		)

		ctx := context.Background()
		assert.Zero(t, value)
		require.NoError(t, app.Start(ctx))
		defer func() {
			require.NoError(t, app.Stop(ctx))
		}()
		assert.Equal(t, 3, value)
	})

	t.Run("with Supply", func(t *testing.T) {
		t.Parallel()

		type A interface {
			WriteString(string) (int, error)
		}

		buf := bytes.NewBuffer(nil)
		var called bool

		ctor := fx.Provide(
			fx.Annotate(
				func(s fmt.Stringer) A {
					return buf
				},
				fx.OnStart(func(_ context.Context, a A, s fmt.Stringer) error {
					a.WriteString(s.String())
					return nil
				}),
			),
		)

		supply := fx.Supply(
			fx.Annotate(
				&asStringer{"supply"},
				fx.OnStart(func(context.Context) error {
					called = true
					return nil
				}),
				fx.As(new(fmt.Stringer)),
			))

		opts := fx.Options(
			ctor,
			supply,
			fx.Invoke(func(A) {}),
		)

		app := fxtest.New(t, opts)
		ctx := context.Background()
		require.False(t, called)
		err := app.Start(ctx)
		require.NoError(t, err)
		require.NoError(t, app.Stop(ctx))
		require.Equal(t, "supply", buf.String())
		require.True(t, called)
	})

	t.Run("with Decorate", func(t *testing.T) {
		t.Parallel()

		type A interface {
			WriteString(string) (int, error)
		}

		buf := bytes.NewBuffer(nil)
		ctor := fx.Provide(func() A { return buf })

		var called bool

		hook := fx.Annotate(
			func(in A) A {
				in.WriteString("decorated")
				return in
			},
			fx.OnStart(func(_ context.Context, _ A) error {
				called = assert.Equal(t, "decorated", buf.String())
				return nil
			}),
		)

		decorated := fx.Decorate(hook)

		opts := fx.Options(
			ctor,
			decorated,
			fx.Invoke(func(A) {}),
		)

		app := fxtest.New(t, opts)
		ctx := context.Background()
		require.NoError(t, app.Start(ctx))
		require.NoError(t, app.Stop(ctx))
		require.True(t, called)
		require.Equal(t, "decorated", buf.String())
	})

	t.Run("with Decorate and tags", func(t *testing.T) {
		t.Parallel()

		type A interface {
			WriteString(string) (int, error)
		}

		buf := bytes.NewBuffer(nil)
		ctor := fx.Provide(
			fx.Annotate(
				func() A { return buf },
				fx.ResultTags(`name:"name"`),
			),
		)

		var called bool

		type hookParam struct {
			fx.In
			A A `name:"name"`
		}

		hook := fx.Annotate(
			func(in A) A {
				in.WriteString("decorated")
				return in
			},
			fx.ParamTags(`name:"name"`),
			fx.ResultTags(`name:"name"`),
			fx.OnStart(func(_ context.Context, _ hookParam) error {
				called = assert.Equal(t, "decorated", buf.String())
				return nil
			}),
		)

		decorated := fx.Decorate(hook)

		opts := fx.Options(
			ctor,
			decorated,
			fx.Invoke(fx.Annotate(func(A) {}, fx.ParamTags(`name:"name"`))),
		)

		app := fxtest.New(t, opts)
		ctx := context.Background()
		require.NoError(t, app.Start(ctx))
		require.NoError(t, app.Stop(ctx))
		require.True(t, called)
		require.Equal(t, "decorated", buf.String())
	})

	t.Run("with Supply and Decorate", func(t *testing.T) {
		t.Parallel()

		type A any

		ch := make(chan string, 3)

		hook := fx.Annotate(
			func(s fmt.Stringer) A { return nil },
			fx.OnStart(func(_ context.Context, s fmt.Stringer) error {
				ch <- "constructor"
				require.Equal(t, "supply", s.String())
				return nil
			}),
		)

		ctor := fx.Provide(hook)

		hook = fx.Annotate(
			&asStringer{"supply"},
			fx.OnStart(func(_ context.Context) error {
				ch <- "supply"
				return nil
			}),
			fx.As(new(fmt.Stringer)),
		)

		supply := fx.Supply(hook)

		hook = fx.Annotate(
			func(in A) A { return in },
			fx.OnStart(func(_ context.Context) error {
				ch <- "decorated"
				return nil
			}),
		)

		decorated := fx.Decorate(hook)

		opts := fx.Options(
			ctor,
			supply,
			decorated,
			fx.Invoke(func(A) {}),
		)

		app := fxtest.New(t, opts)
		ctx := context.Background()
		err := app.Start(ctx)
		require.NoError(t, err)
		require.NoError(t, app.Stop(ctx))
		close(ch)

		require.Equal(t, "supply", <-ch)
		require.Equal(t, "constructor", <-ch)
		require.Equal(t, "decorated", <-ch)
	})

	t.Run("Annotated params work with lifecycle hook annotations", func(t *testing.T) {
		t.Parallel()

		type paramStruct struct {
			fx.In
			A *a `name:"a" optional:"true"`
			B *b `name:"b"`
		}

		app := fxtest.New(t,
			fx.Provide(
				fx.Annotate(newB, fx.ParamTags(`optional:"true"`)),
				fx.Annotate(func(a *a, b *b) any { return nil },
					fx.ParamTags(`name:"a" optional:"true"`, `name:"b"`),
					fx.ResultTags(`name:"nil"`),
					fx.OnStart(func(_ paramStruct) error {
						return nil
					}),
					fx.OnStop(func(_ paramStruct) error {
						return nil
					}),
				),
			),
			fx.Invoke(newC),
		)
		defer app.RequireStart().RequireStop()
		require.NoError(t, app.Err())
	})

	t.Run("provide with annotated results and lifecycle hook appended", func(t *testing.T) {
		t.Parallel()

		type firstAHookParam struct {
			fx.In
			Ctx context.Context
			A   *a `name:"firstA"`
		}
		type secondAHookParam struct {
			fx.In
			A   *a `name:"secondA"`
			Ctx context.Context
		}
		type thirdAHookParam struct {
			fx.In
			Ctx context.Context
			A   *a `name:"thirdA"`
		}

		app := fxtest.New(t,
			fx.Provide(
				fx.Annotate(func() *a {
					return &a{}
				}, fx.ResultTags(`name:"firstA"`),
					fx.OnStart(func(param firstAHookParam) error {
						require.NotNil(t, param.Ctx, "context should be given")
						return nil
					})),
				fx.Annotate(func() *a {
					return &a{}
				}, fx.ResultTags(`name:"secondA"`),
					fx.OnStart(func(param secondAHookParam) error {
						require.NotNil(t, param.Ctx, "context not correctly injected")
						return nil
					})),
				fx.Annotate(func() *a {
					return &a{}
				}, fx.ResultTags(`name:"thirdA"`),
					fx.OnStart(func(param thirdAHookParam) error {
						require.NotNil(t, param.Ctx, "context not correctly injected")
						return nil
					})),
				fx.Annotate(func(a1 *a, a2 *a, a3 *a) *b {
					return &b{a1}
				}, fx.ParamTags(
					`name:"firstA"`,
					`name:"secondA"`,
					`name:"thirdA"`,
				)),
			),
			fx.Invoke(newC),
		)

		require.NoError(t, app.Err())
		defer app.RequireStart().RequireStop()
	})

	t.Run("provide with optional params and lifecycle hook", func(t *testing.T) {
		type taggedHookParam struct {
			fx.In
			Ctx context.Context
			A   *a `optional:"true"`
		}
		t.Parallel()
		app := fxtest.New(t,
			fx.Provide(
				fx.Annotate(
					newB,
					fx.ParamTags(`optional:"true"`),
					fx.OnStart(func(tp taggedHookParam, B *b) {
						fmt.Println(tp.A)
						require.NotNil(t, tp.Ctx, "context not correctly injected")
					}),
				),
			),
			fx.Invoke(newC),
		)

		require.NoError(t, app.Err())
		defer app.RequireStart().RequireStop()
	})
}

func TestHookAnnotationFailures(t *testing.T) {
	t.Parallel()
	validateApp := func(t *testing.T, opts ...fx.Option) error {
		return fx.ValidateApp(
			append(opts, fx.Logger(fxtest.NewTestPrinter(t)))...,
		)
	}

	type (
		A any
		B any
	)

	type namedAndGroupHookParams struct {
		fx.In
		Ctx context.Context
		A   *A `name:"a" group:"groupA"`
	}

	type namedHookParam struct {
		fx.In
		Ctx context.Context
		A   *A `name:"a"`
	}

	type groupedHookParam struct {
		fx.In
		Ctx context.Context
		A   *A `group:"groupA"`
	}

	table := []struct {
		name        string
		annotation  any
		extraOpts   fx.Option
		useNew      bool
		errContains string
	}{
		{
			name:        "with unprovided dependency",
			errContains: "hook function takes in a parameter of",
			useNew:      true,
			annotation: fx.Annotate(
				func() A { return nil },
				fx.OnStart(func(context.Context, B) error {
					return nil
				}),
			),
		},
		{
			name:        "with hook that errors",
			errContains: "hook failed",
			useNew:      true,
			annotation: fx.Annotate(
				func() (A, error) { return nil, nil },
				fx.OnStart(func(context.Context) error {
					return errors.New("hook failed")
				}),
			),
		},
		{
			name:        "with multiple hooks of the same type",
			errContains: `cannot apply more than one "OnStart" hook annotation`,
			annotation: fx.Annotate(
				func() A { return nil },
				fx.OnStart(func(context.Context) error { return nil }),
				fx.OnStart(func(context.Context) error { return nil }),
			),
		},
		{
			name:        "with constructor that errors",
			errContains: "hooks should not be installed",
			useNew:      true,
			annotation: fx.Annotate(
				func() (A, error) {
					return nil, errors.New("hooks should not be installed")
				},
				fx.OnStart(func(context.Context) error {
					require.FailNow(t, "hook should not be called")
					return nil
				}),
			),
		},
		{
			name:        "without a function target",
			errContains: "must provide function",
			annotation: fx.Annotate(
				func() A { return nil },
				fx.OnStart(&struct{}{}),
			),
		},
		{
			name:        "invalid return: non-error return",
			errContains: "optional hook return may only be an error",
			annotation: fx.Annotate(
				func() A { return nil },
				fx.OnStart(func(context.Context) any {
					return nil
				}),
			),
		},
		{
			name:        "invalid return: too many returns",
			errContains: "optional hook return may only be an error",
			annotation: fx.Annotate(
				func() A { return nil },
				fx.OnStart(func(context.Context) (any, any) {
					return nil, nil
				}),
			),
		},
		{
			name:        "with variactic hook",
			errContains: "must not accept variadic",
			annotation: fx.Annotate(
				func() A { return nil },
				fx.OnStart(func(context.Context, ...A) error {
					return nil
				}),
			),
		},
		{
			name:        "with nil hook target",
			errContains: "cannot use nil function",
			annotation: fx.Annotate(
				func() A { return nil },
				fx.OnStop(nil),
			),
		},
		{
			name:        "cannot pull in any extra dependency other than params or results of the annotated function",
			errContains: "hook function takes in a parameter of \"fx_test.B\"",
			useNew:      true,
			annotation: fx.Annotate(
				func(s string) A { return nil },
				fx.OnStart(func(b B) error { return nil }),
			),
			extraOpts: fx.Options(
				fx.Provide(func() string { return "test" }),
				fx.Provide(func() B { return nil }),
			),
		},
		{
			name:        "cannot pull in a dependency when it's not properly named",
			errContains: "hook function takes in a parameter of \"*fx_test.A `name:\"a\"`\"",
			useNew:      true,
			annotation: fx.Annotate(
				func(s A) A { return nil },
				fx.OnStart(func(b namedHookParam) error { return nil }),
			),
		},
		{
			name:        "cannot pull in a dependency when it's not properly grouped",
			errContains: "hook function takes in a parameter of \"*fx_test.A `group:\"groupA\"`",
			useNew:      true,
			annotation: fx.Annotate(
				func(s A) A { return nil },
				fx.OnStart(func(b groupedHookParam) error { return nil }),
			),
		},
		{
			name:        "cannot pull in a dependency when it's not properly named and grouped",
			errContains: "hook function takes in a parameter of \"*fx_test.A `name:\"a\" group:\"groupA\"`",
			useNew:      true,
			annotation: fx.Annotate(
				func(s A) A { return nil },
				fx.OnStart(func(b namedAndGroupHookParams) error { return nil }),
			),
		},
	}

	for _, tt := range table {
		t.Run(tt.name, func(t *testing.T) {
			t.Parallel()
			opts := fx.Options(
				fx.Provide(tt.annotation),
				fx.Invoke(func(A) {}),
			)

			if tt.extraOpts != nil {
				opts = fx.Options(opts, tt.extraOpts)
			}

			if !tt.useNew {
				err := validateApp(t, opts)
				require.Error(t, err)
				require.Contains(t, err.Error(), tt.errContains)
				return
			}

			app := NewForTest(t, opts)
			err := app.Start(context.Background())
			require.Error(t, err)
			require.Contains(t, err.Error(), tt.errContains)
		})
	}
}

func TestHookAnnotationFunctionFlexibility(t *testing.T) {
	type A any

	table := []struct {
		name       string
		annotation any
	}{
		{
			name: "without error return",
			annotation: fx.Annotate(
				func(called *atomic.Bool) A { return nil },
				fx.OnStart(func(_ context.Context, called *atomic.Bool) {
					called.Store(true)
				}),
			),
		},
		{
			name: "without context param",
			annotation: fx.Annotate(
				func(called *atomic.Bool) A { return nil },
				fx.OnStart(func(called *atomic.Bool) error {
					called.Store(true)
					return nil
				}),
			),
		},
		{
			name: "without context param or error return",
			annotation: fx.Annotate(
				func(called *atomic.Bool) A { return nil },
				fx.OnStart(func(called *atomic.Bool) {
					called.Store(true)
				}),
			),
		},
		{
			name: "with context param and error return",
			annotation: fx.Annotate(
				func(called *atomic.Bool) A { return nil },
				fx.OnStart(func(_ context.Context, called *atomic.Bool) error {
					called.Store(true)
					return nil
				}),
			),
		},
	}

	for _, tt := range table {
		t.Run(tt.name, func(t *testing.T) {
			var (
				called atomic.Bool
				opts   = fx.Options(
					fx.Provide(tt.annotation),
					fx.Supply(&called),
					fx.Invoke(func(A) {}),
				)
			)

			fxtest.New(t, opts).RequireStart().RequireStop()
			require.True(t, called.Load())
		})
	}
}


================================================
FILE: app.go
================================================
// Copyright (c) 2020-2021 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

package fx

import (
	"bytes"
	"context"
	"errors"
	"fmt"
	"os"
	"reflect"
	"strings"
	"time"

	"go.uber.org/dig"
	"go.uber.org/fx/fxevent"
	"go.uber.org/fx/internal/fxclock"
	"go.uber.org/fx/internal/fxlog"
	"go.uber.org/fx/internal/fxreflect"
	"go.uber.org/fx/internal/lifecycle"
	"go.uber.org/multierr"
)

// DefaultTimeout is the default timeout for starting or stopping an
// application. It can be configured with the [StartTimeout] and [StopTimeout]
// options.
const DefaultTimeout = 15 * time.Second

// An Option specifies the behavior of the application.
// This is the primary means by which you interface with Fx.
//
// Zero or more options are specified at startup with [New].
// Options cannot be changed once an application has been initialized.
// Options may be grouped into a single option using the [Options] function.
// A group of options providing a logical unit of functionality
// may use [Module] to name that functionality
// and scope certain operations to within that module.
type Option interface {
	fmt.Stringer

	apply(*module)
}

// Error registers any number of errors with the application to short-circuit
// startup. If more than one error is given, the errors are combined into a
// single error.
//
// Similar to invocations, errors are applied in order. All Provide and Invoke
// options registered before or after an Error option will not be applied.
func Error(errs ...error) Option {
	return errorOption(errs)
}

type errorOption []error

func (errs errorOption) apply(mod *module) {
	mod.app.err = multierr.Append(mod.app.err, multierr.Combine(errs...))
}

func (errs errorOption) String() string {
	return fmt.Sprintf("fx.Error(%v)", multierr.Combine(errs...))
}

// Options bundles a group of options together into a single option.
//
// Use Options to group together options that don't belong in a [Module].
//
//	var loggingAndMetrics = fx.Options(
//		logging.Module,
//		metrics.Module,
//		fx.Invoke(func(logger *log.Logger) {
//			app.globalLogger = logger
//		}),
//	)
func Options(opts ...Option) Option {
	return optionGroup(opts)
}

type optionGroup []Option

func (og optionGroup) apply(mod *module) {
	for _, opt := range og {
		opt.apply(mod)
	}
}

func (og optionGroup) String() string {
	items := make([]string, len(og))
	for i, opt := range og {
		items[i] = fmt.Sprint(opt)
	}
	return fmt.Sprintf("fx.Options(%s)", strings.Join(items, ", "))
}

// StartTimeout changes the application's start timeout.
// This controls the total time that all [OnStart] hooks have to complete.
// If the timeout is exceeded, the application will fail to start.
//
// Defaults to [DefaultTimeout].
func StartTimeout(v time.Duration) Option {
	return startTimeoutOption(v)
}

type startTimeoutOption time.Duration

func (t startTimeoutOption) apply(m *module) {
	if m.parent != nil {
		m.app.err = fmt.Errorf("fx.StartTimeout Option should be passed to top-level App, " +
			"not to fx.Module")
	} else {
		m.app.startTimeout = time.Duration(t)
	}
}

func (t startTimeoutOption) String() string {
	return fmt.Sprintf("fx.StartTimeout(%v)", time.Duration(t))
}

// StopTimeout changes the application's stop timeout.
// This controls the total time that all [OnStop] hooks have to complete.
// If the timeout is exceeded, the application will exit early.
//
// Defaults to [DefaultTimeout].
func StopTimeout(v time.Duration) Option {
	return stopTimeoutOption(v)
}

type stopTimeoutOption time.Duration

func (t stopTimeoutOption) apply(m *module) {
	if m.parent != nil {
		m.app.err = fmt.Errorf("fx.StopTimeout Option should be passed to top-level App, " +
			"not to fx.Module")
	} else {
		m.app.stopTimeout = time.Duration(t)
	}
}

func (t stopTimeoutOption) String() string {
	return fmt.Sprintf("fx.StopTimeout(%v)", time.Duration(t))
}

// RecoverFromPanics causes panics that occur in functions given to [Provide],
// [Decorate], and [Invoke] to be recovered from.
// This error can be retrieved as any other error, by using (*App).Err().
func RecoverFromPanics() Option {
	return recoverFromPanicsOption{}
}

type recoverFromPanicsOption struct{}

func (o recoverFromPanicsOption) apply(m *module) {
	if m.parent != nil {
		m.app.err = fmt.Errorf("fx.RecoverFromPanics Option should be passed to top-level " +
			"App, not to fx.Module")
	} else {
		m.app.recoverFromPanics = true
	}
}

func (o recoverFromPanicsOption) String() string {
	return "fx.RecoverFromPanics()"
}

// WithLogger specifies the [fxevent.Logger] used by Fx to log its own events
// (e.g. a constructor was provided, a function was invoked, etc.).
//
// The argument to this is a constructor with one of the following return
// types:
//
//	fxevent.Logger
//	(fxevent.Logger, error)
//
// The constructor may depend on any other types provided to the application.
// For example,
//
//	WithLogger(func(logger *zap.Logger) fxevent.Logger {
//	  return &fxevent.ZapLogger{Logger: logger}
//	})
//
// If specified, Fx will construct the logger and log all its events to the
// specified logger.
//
// If Fx fails to build the logger, or no logger is specified, it will fall back to
// [fxevent.ConsoleLogger] configured to write to stderr.
func WithLogger(constructor any) Option {
	return withLoggerOption{
		constructor: constructor,
		Stack:       fxreflect.CallerStack(1, 0),
	}
}

type withLoggerOption struct {
	constructor any
	Stack       fxreflect.Stack
}

func (l withLoggerOption) apply(m *module) {
	m.logConstructor = &provide{
		Target: l.constructor,
		Stack:  l.Stack,
	}
}

func (l withLoggerOption) String() string {
	return fmt.Sprintf("fx.WithLogger(%s)", fxreflect.FuncName(l.constructor))
}

// Printer is the interface required by Fx's logging backend. It's implemented
// by most loggers, including the one bundled with the standard library.
//
// Note, this will be deprecated in a future release.
// Prefer to use [fxevent.Logger] instead.
type Printer interface {
	Printf(string, ...any)
}

// Logger redirects the application's log output to the provided printer.
//
// Prefer to use [WithLogger] instead.
func Logger(p Printer) Option {
	return loggerOption{p}
}

type loggerOption struct{ p Printer }

func (l loggerOption) apply(m *module) {
	if m.parent != nil {
		m.app.err = fmt.Errorf("fx.Logger Option should be passed to top-level App, " +
			"not to fx.Module")
	} else {
		np := writerFromPrinter(l.p)
		m.log = fxlog.DefaultLogger(np) // assuming np is thread-safe.
	}
}

func (l loggerOption) String() string {
	return fmt.Sprintf("fx.Logger(%v)", l.p)
}

// NopLogger disables the application's log output.
//
// Note that this makes some failures difficult to debug,
// since no errors are printed to console.
// Prefer to log to an in-memory buffer instead.
var NopLogger = WithLogger(func() fxevent.Logger { return fxevent.NopLogger })

// An App is a modular application built around dependency injection. Most
// users will only need to use the New constructor and the all-in-one Run
// convenience method. In more unusual cases, users may need to use the Err,
// Start, Done, and Stop methods by hand instead of relying on Run.
//
// [New] creates and initializes an App. All applications begin with a
// constructor for the Lifecycle type already registered.
//
// In addition to that built-in functionality, users typically pass a handful
// of [Provide] options and one or more [Invoke] options. The Provide options
// teach the application how to instantiate a variety of types, and the Invoke
// options describe how to initialize the application.
//
// When created, the application immediately executes all the functions passed
// via Invoke options. To supply these functions with the parameters they
// need, the application looks for constructors that return the appropriate
// types; if constructors for any required types are missing or any
// invocations return an error, the application will fail to start (and Err
// will return a descriptive error message).
//
// Once all the invocations (and any required constructors) have been called,
// New returns and the application is ready to be started using Run or Start.
// On startup, it executes any OnStart hooks registered with its Lifecycle.
// OnStart hooks are executed one at a time, in order, and must all complete
// within a configurable deadline (by default, 15 seconds). For details on the
// order in which OnStart hooks are executed, see the documentation for the
// Start method.
//
// At this point, the application has successfully started up. If started via
// Run, it will continue operating until it receives a shutdown signal from
// Done (see the [App.Done] documentation for details); if started explicitly via
// Start, it will operate until the user calls Stop. On shutdown, OnStop hooks
// execute one at a time, in reverse order, and must all complete within a
// configurable deadline (again, 15 seconds by default).
type App struct {
	err       error
	clock     fxclock.Clock
	lifecycle *lifecycleWrapper

	container *dig.Container
	root      *module

	// Timeouts used
	startTimeout time.Duration
	stopTimeout  time.Duration
	// Decides how we react to errors when building the graph.
	errorHooks []ErrorHandler
	validate   bool
	// Whether to recover from panics in Dig container
	recoverFromPanics bool

	// Used to signal shutdowns.
	receivers signalReceivers

	osExit func(code int) // os.Exit override; used for testing only
}

// provide is a single constructor provided to Fx.
type provide struct {
	// Constructor provided to Fx. This may be an fx.Annotated.
	Target any

	// Stack trace of where this provide was made.
	Stack fxreflect.Stack

	// IsSupply is true when the Target constructor was emitted by fx.Supply.
	IsSupply   bool
	SupplyType reflect.Type // set only if IsSupply

	// Set if the type should be provided at private scope.
	Private bool
}

// invoke is a single invocation request to Fx.
type invoke struct {
	// Function to invoke.
	Target any

	// Stack trace of where this invoke was made.
	Stack fxreflect.Stack
}

// ErrorHandler handles Fx application startup errors.
// Register these with [ErrorHook].
// If specified, and the application fails to start up,
// the failure will still cause a crash,
// but you'll have a chance to log the error or take some other action.
type ErrorHandler interface {
	HandleError(error)
}

// ErrorHook registers error handlers that implement error handling functions.
// They are executed on invoke failures. Passing multiple ErrorHandlers appends
// the new handlers to the application's existing list.
func ErrorHook(funcs ...ErrorHandler) Option {
	return errorHookOption(funcs)
}

type errorHookOption []ErrorHandler

func (eho errorHookOption) apply(m *module) {
	m.app.errorHooks = append(m.app.errorHooks, eho...)
}

func (eho errorHookOption) String() string {
	items := make([]string, len(eho))
	for i, eh := range eho {
		items[i] = fmt.Sprint(eh)
	}
	return fmt.Sprintf("fx.ErrorHook(%v)", strings.Join(items, ", "))
}

type errorHandlerList []ErrorHandler

func (ehl errorHandlerList) HandleError(err error) {
	for _, eh := range ehl {
		eh.HandleError(err)
	}
}

// validate sets *App into validation mode without running invoked functions.
func validate(validate bool) Option {
	return &validateOption{
		validate: validate,
	}
}

type validateOption struct {
	validate bool
}

func (o validateOption) apply(m *module) {
	if m.parent != nil {
		m.app.err = fmt.Errorf("fx.validate Option should be passed to top-level App, " +
			"not to fx.Module")
	} else {
		m.app.validate = o.validate
	}
}

func (o validateOption) String() string {
	return fmt.Sprintf("fx.validate(%v)", o.validate)
}

// ValidateApp validates that supplied graph would run and is not missing any dependencies. This
// method does not invoke actual input functions.
func ValidateApp(opts ...Option) error {
	opts = append(opts, validate(true))
	app := New(opts...)

	return app.Err()
}

// New creates and initializes an App, immediately executing any functions
// registered via [Invoke] options. See the documentation of the App struct for
// details on the application's initialization, startup, and shutdown logic.
func New(opts ...Option) *App {
	logger := fxlog.DefaultLogger(os.Stderr)

	app := &App{
		clock:        fxclock.System,
		startTimeout: DefaultTimeout,
		stopTimeout:  DefaultTimeout,
		receivers:    newSignalReceivers(),
	}
	app.root = &module{
		app: app,
		// We start with a logger that writes to stderr. One of the
		// following three things can change this:
		//
		// - fx.Logger was provided to change the output stream
		// - fx.WithLogger was provided to change the logger
		//   implementation
		// - Both, fx.Logger and fx.WithLogger were provided
		//
		// The first two cases are straightforward: we use what the
		// user gave us. For the last case, however, we need to fall
		// back to what was provided to fx.Logger if fx.WithLogger
		// fails.
		log:   logger,
		trace: []string{fxreflect.CallerStack(1, 2)[0].String()},
	}

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

	// There are a few levels of wrapping on the lifecycle here. To quickly
	// cover them:
	//
	// - lifecycleWrapper ensures that we don't unintentionally expose the
	//   Start and Stop methods of the internal lifecycle.Lifecycle type
	// - lifecycleWrapper also adapts the internal lifecycle.Hook type into
	//   the public fx.Hook type.
	// - appLogger ensures that the lifecycle always logs events to the
	//   "current" logger associated with the fx.App.
	app.lifecycle = &lifecycleWrapper{
		lifecycle.New(appLogger{app}, app.clock),
	}

	containerOptions := []dig.Option{
		dig.DeferAcyclicVerification(),
		dig.DryRun(app.validate),
	}

	if app.recoverFromPanics {
		containerOptions = append(containerOptions, dig.RecoverFromPanics())
	}

	app.container = dig.New(containerOptions...)
	app.root.build(app, app.container)

	// Provide Fx types first to increase the chance a custom logger
	// can be successfully built in the face of unrelated DI failure.
	// E.g., for a custom logger that relies on the Lifecycle type.
	frames := fxreflect.CallerStack(0, 0) // include New in the stack for default Provides
	app.root.provide(provide{
		Target: func() Lifecycle { return app.lifecycle },
		Stack:  frames,
	})
	app.root.provide(provide{Target: app.shutdowner, Stack: frames})
	app.root.provide(provide{Target: app.dotGraph, Stack: frames})
	app.root.provideAll()

	// Run decorators before executing any Invokes
	// (including the ones inside installAllEventLoggers).
	app.err = multierr.Append(app.err, app.root.decorateAll())

	// If you are thinking about returning here after provides: do not (just yet)!
	// If a custom logger was being used, we're still buffering messages.
	// We'll want to flush them to the logger.

	// custom app logger will be initialized by the root module.
	app.root.installAllEventLoggers()

	// This error might have come from the provide loop above. We've
	// already flushed to the custom logger, so we can return.
	if app.err != nil {
		return app
	}

	if err := app.root.invokeAll(); err != nil {
		app.err = err

		if dig.CanVisualizeError(err) {
			var b bytes.Buffer
			dig.Visualize(app.container, &b, dig.VisualizeError(err))
			err = errorWithGraph{
				graph: b.String(),
				err:   err,
			}
		}
		errorHandlerList(app.errorHooks).HandleError(err)
	}

	return app
}

func (app *App) log() fxevent.Logger {
	return app.root.log
}

// DotGraph contains a DOT language visualization of the dependency graph in
// an Fx application. It is provided in the container by default at
// initialization. On failure to build the dependency graph, it is attached
// to the error and if possible, colorized to highlight the root cause of the
// failure.
//
// Note that DotGraph does not yet recognize [Decorate] and [Replace].
type DotGraph string

type errWithGraph interface {
	Graph() DotGraph
}

type errorWithGraph struct {
	graph string
	err   error
}

func (err errorWithGraph) Graph() DotGraph {
	return DotGraph(err.graph)
}

func (err errorWithGraph) Error() string {
	return err.err.Error()
}

// VisualizeError returns the visualization of the error if available.
//
// Note that VisualizeError does not yet recognize [Decorate] and [Replace].
func VisualizeError(err error) (string, error) {
	var erg errWithGraph
	if errors.As(err, &erg) {
		if g := erg.Graph(); g != "" {
			return string(g), nil
		}
	}
	return "", errors.New("unable to visualize error")
}

// Exits the application with the given exit code.
func (app *App) exit(code int) {
	osExit := os.Exit
	if app.osExit != nil {
		osExit = app.osExit
	}
	osExit(code)
}

// Run starts the application, blocks on the signals channel, and then
// gracefully shuts the application down. It uses [DefaultTimeout] to set a
// deadline for application startup and shutdown, unless the user has
// configured different timeouts with the [StartTimeout] or [StopTimeout] options.
// It's designed to make typical applications simple to run.
// The minimal Fx application looks like this:
//
//	fx.New().Run()
//
// All of Run's functionality is implemented in terms of the exported
// Start, Done, and Stop methods. Applications with more specialized needs
// can use those methods directly instead of relying on Run.
//
// After the application has started,
// it can be shut down by sending a signal or calling [Shutdowner.Shutdown].
// On successful shutdown, whether initiated by a signal or by the user,
// Run will return to the caller, allowing it to exit cleanly.
// Run will exit with a non-zero status code
// if startup or shutdown operations fail,
// or if the [Shutdowner] supplied a non-zero exit code.
func (app *App) Run() {
	// Historically, we do not os.Exit(0) even though most applications
	// cede control to Fx with they call app.Run. To avoid a breaking
	// change, never os.Exit for success.
	if code := app.run(app.Wait); code != 0 {
		app.exit(code)
	}
}

func (app *App) run(done func() <-chan ShutdownSignal) (exitCode int) {
	startCtx, cancel := app.clock.WithTimeout(context.Background(), app.StartTimeout())
	defer cancel()

	if err := app.Start(startCtx); err != nil {
		return 1
	}

	sig := <-done()
	app.log().LogEvent(&fxevent.Stopping{Signal: sig.Signal})
	exitCode = sig.ExitCode

	stopCtx, cancel := app.clock.WithTimeout(context.Background(), app.StopTimeout())
	defer cancel()

	if err := app.Stop(stopCtx); err != nil {
		return 1
	}

	return exitCode
}

// Err returns any error encountered during New's initialization. See the
// documentation of the New method for details, but typical errors include
// missing constructors, circular dependencies, constructor errors, and
// invocation errors.
//
// Most users won't need to use this method, since both Run and Start
// short-circuit if initialization failed.
func (app *App) Err() error {
	return app.err
}

var (
	_onStartHook = "OnStart"
	_onStopHook  = "OnStop"
)

// Start kicks off all long-running goroutines, like network servers or
// message queue consumers. It does this by interacting with the application's
// Lifecycle.
//
// By taking a dependency on the Lifecycle type, some of the user-supplied
// functions called during initialization may have registered start and stop
// hooks. Because initialization calls constructors serially and in dependency
// order, hooks are naturally registered in serial and dependency order too.
//
// Start executes all OnStart hooks registered with the application's
// Lifecycle, one at a time and in order. This ensures that each constructor's
// start hooks aren't executed until all its dependencies' start hooks
// complete. If any of the start hooks return an error, Start short-circuits,
// calls Stop, and returns the inciting error.
//
// Note that Start short-circuits immediately if the New constructor
// encountered any errors in application initialization.
func (app *App) Start(ctx context.Context) (err error) {
	defer func() {
		app.log().LogEvent(&fxevent.Started{Err: err})
	}()

	if app.err != nil {
		// Some provides failed, short-circuit immediately.
		return app.err
	}

	return withTimeout(ctx, &withTimeoutParams{
		hook:      _onStartHook,
		callback:  app.start,
		lifecycle: app.lifecycle,
		log:       app.log(),
	})
}

// withRollback will execute an anonymous function with a given context.
// if the anon func returns an error, rollback methods will be called and related events emitted
func (app *App) withRollback(
	ctx context.Context,
	f func(context.Context) error,
) error {
	if err := f(ctx); err != nil {
		app.log().LogEvent(&fxevent.RollingBack{StartErr: err})

		stopErr := app.lifecycle.Stop(ctx)
		app.log().LogEvent(&fxevent.RolledBack{Err: stopErr})

		if stopErr != nil {
			return multierr.Append(err, stopErr)
		}

		return err
	}

	return nil
}

func (app *App) start(ctx context.Context) error {
	return app.withRollback(ctx, func(ctx context.Context) error {
		if err := app.lifecycle.Start(ctx); err != nil {
			return err
		}
		return nil
	})
}

// Stop gracefully stops the application. It executes any registered OnStop
// hooks in reverse order, so that each constructor's stop hooks are called
// before its dependencies' stop hooks.
//
// If the application didn't start cleanly, only hooks whose OnStart phase was
// called are executed. However, all those hooks are executed, even if some
// fail.
func (app *App) Stop(ctx context.Context) (err error) {
	defer func() {
		app.log().LogEvent(&fxevent.Stopped{Err: err})
	}()

	cb := func(ctx context.Context) error {
		defer app.receivers.Stop(ctx)
		return app.lifecycle.Stop(ctx)
	}

	return withTimeout(ctx, &withTimeoutParams{
		hook:      _onStopHook,
		callback:  cb,
		lifecycle: app.lifecycle,
		log:       app.log(),
	})
}

// Done returns a channel of signals to block on after starting the
// application. Applications listen for the SIGINT and SIGTERM signals; during
// development, users can send the application SIGTERM by pressing Ctrl-C in
// the same terminal as the running process.
//
// Alternatively, a signal can be broadcast to all done channels manually by
// using the Shutdown functionality (see the [Shutdowner] documentation for details).
func (app *App) Done() <-chan os.Signal {
	app.receivers.Start() // No-op if running
	return app.receivers.Done()
}

// Wait returns a channel of [ShutdownSignal] to block on after starting the
// application and function, similar to [App.Done], but with a minor difference:
// if the app was shut down via [Shutdowner.Shutdown],
// the exit code (if provied via [ExitCode]) will be available
// in the [ShutdownSignal] struct.
// Otherwise, the signal that was received will be set.
func (app *App) Wait() <-chan ShutdownSignal {
	app.receivers.Start() // No-op if running
	return app.receivers.Wait()
}

// StartTimeout returns the configured startup timeout.
// This defaults to [DefaultTimeout], and can be changed with the
// [StartTimeout] option.
func (app *App) StartTimeout() time.Duration {
	return app.startTimeout
}

// StopTimeout returns the configured shutdown timeout.
// This defaults to [DefaultTimeout], and can be changed with the
// [StopTimeout] option.
func (app *App) StopTimeout() time.Duration {
	return app.stopTimeout
}

func (app *App) dotGraph() (DotGraph, error) {
	var b bytes.Buffer
	err := dig.Visualize(app.container, &b)
	return DotGraph(b.String()), err
}

type withTimeoutParams struct {
	log       fxevent.Logger
	hook      string
	callback  func(context.Context) error
	lifecycle *lifecycleWrapper
}

// errHookCallbackExited is returned when a hook callback does not finish executing
var errHookCallbackExited = errors.New("goroutine exited without returning")

func withTimeout(ctx context.Context, param *withTimeoutParams) error {
	c := make(chan error, 1)
	go func() {
		// If runtime.Goexit() is called from within the callback
		// then nothing is written to the chan.
		// However the defer will still be called, so we can write to the chan,
		// to avoid hanging until the timeout is reached.
		callbackExited := false
		defer func() {
			if !callbackExited {
				c <- errHookCallbackExited
			}
		}()

		c <- param.callback(ctx)
		callbackExited = true
	}()

	var err error

	select {
	case <-ctx.Done():
		err = ctx.Err()
	case err = <-c:
		// If the context finished at the same time as the callback
		// prefer the context error.
		// This eliminates non-determinism in select-case selection.
		if ctx.Err() != nil {
			err = ctx.Err()
		}
	}

	return err
}

// appLogger logs events to the given Fx app's "current" logger.
//
// Use this with lifecycle, for example, to ensure that events always go to the
// correct logger.
type appLogger struct{ app *App }

func (l appLogger) LogEvent(ev fxevent.Event) {
	l.app.log().LogEvent(ev)
}


================================================
FILE: app_internal_test.go
================================================
// Copyright (c) 2019-2021 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

package fx

import (
	"context"
	"errors"
	"fmt"
	"os"
	"sync"
	"testing"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
	"go.uber.org/fx/fxevent"
	"go.uber.org/fx/internal/fxclock"
	"go.uber.org/fx/internal/fxlog"
	"go.uber.org/fx/internal/fxreflect"
)

func TestAppRun(t *testing.T) {
	t.Parallel()

	spy := new(fxlog.Spy)
	app := New(
		WithLogger(func() fxevent.Logger { return spy }),
	)
	done := make(chan ShutdownSignal)

	var wg sync.WaitGroup
	wg.Add(1)
	go func() {
		defer wg.Done()
		app.run(func() <-chan ShutdownSignal { return done })
	}()

	done <- ShutdownSignal{Signal: _sigINT}
	wg.Wait()

	assert.Equal(t, []string{
		"Provided",
		"Provided",
		"Provided",
		"LoggerInitialized",
		"Started",
		"Stopping",
		"Stopped",
	}, spy.EventTypes())
}

// TestValidateString verifies private option. Public options are tested in app_test.go.
func TestValidateString(t *testing.T) {
	t.Parallel()

	stringer, ok := validate(true).(fmt.Stringer)
	require.True(t, ok, "option must implement stringer")
	assert.Equal(t, "fx.validate(true)", stringer.String())
}

// WithExit is an internal option available only to tests defined in this
// package. It changes how os.Exit behaves for the application.
func WithExit(f func(int)) Option {
	return withExitOption(f)
}

type withExitOption func(int)

func (o withExitOption) String() string {
	return fmt.Sprintf("WithExit(%v)", fxreflect.FuncName(o))
}

func (o withExitOption) apply(m *module) {
	m.app.osExit = o
}

// WithClock specifies how Fx accesses time operations.
//
// This is an internal option available only to tests defined in this package.
func WithClock(clock fxclock.Clock) Option {
	return withClockOption{clock}
}

type withClockOption struct{ clock fxclock.Clock }

func (o withClockOption) apply(m *module) {
	m.app.clock = o.clock
}

func (o withClockOption) String() string {
	return fmt.Sprintf("WithClock(%v)", o.clock)
}

func TestAnnotationError(t *testing.T) {
	wantErr := errors.New("want error")
	err := &annotationError{
		err: wantErr,
	}
	require.Error(t, err)
	assert.ErrorIs(t, err, wantErr)
	assert.Contains(t, err.Error(), wantErr.Error())
}

// TestStartDoesNotRegisterSignals verifies that signal.Notify is not called
// when a user starts an app. signal.Notify should only be called when the
// .Wait/.Done are called. Note that app.Run calls .Wait() implicitly.
func TestStartDoesNotRegisterSignals(t *testing.T) {
	app := New()
	calledNotify := false

	// Mock notify function to spy when this is called.
	app.receivers.notify = func(c chan<- os.Signal, sig ...os.Signal) {
		calledNotify = true
	}
	app.receivers.stopNotify = func(c chan<- os.Signal) {}

	app.Start(context.Background())
	defer app.Stop(context.Background())
	assert.False(t, calledNotify, "notify should not be called when app starts")

	_ = app.Wait() // User signals intent have fx listen for signals. This should call notify
	assert.True(t, calledNotify, "notify should be called after Wait")
}


================================================
FILE: app_test.go
================================================
// Copyright (c) 2023 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

package fx_test

import (
	"bytes"
	"context"
	"errors"
	"fmt"
	"io"
	"log"
	"os"
	"reflect"
	"regexp"
	"runtime"
	"strings"
	"testing"
	"time"

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

	. "go.uber.org/fx"
	"go.uber.org/fx/fxevent"
	"go.uber.org/fx/fxtest"
	"go.uber.org/fx/internal/fxclock"
	"go.uber.org/fx/internal/fxlog"
	"go.uber.org/goleak"
	"go.uber.org/multierr"
	"go.uber.org/zap"
)

func NewForTest(tb testing.TB, opts ...Option) *App {
	testOpts := []Option{
		// Provide both: Logger and WithLogger so that if the test
		// WithLogger fails, we don't pollute stderr.
		Logger(fxtest.NewTestPrinter(tb)),
		fxtest.WithTestLogger(tb),
	}
	opts = append(testOpts, opts...)

	return New(opts...)
}

func NewSpied(opts ...Option) (*App, *fxlog.Spy) {
	spy := new(fxlog.Spy)
	opts = append([]Option{
		WithLogger(func() fxevent.Logger { return spy }),
	}, opts...)
	return New(opts...), spy
}

func validateTestApp(tb testing.TB, opts ...Option) error {
	testOpts := []Option{
		// Provide both: Logger and WithLogger so that if the test
		// WithLogger fails, we don't pollute stderr.
		Logger(fxtest.NewTestPrinter(tb)),
		fxtest.WithTestLogger(tb),
	}
	opts = append(testOpts, opts...)

	return ValidateApp(opts...)
}

func TestNewApp(t *testing.T) {
	t.Parallel()

	t.Run("ProvidesLifecycleAndShutdowner", func(t *testing.T) {
		t.Parallel()

		var (
			l Lifecycle
			s Shutdowner
		)
		fxtest.New(
			t,
			Populate(&l, &s),
		)
		assert.NotNil(t, l)
		assert.NotNil(t, s)
	})

	t.Run("OptionsHappensBeforeProvides", func(t *testing.T) {
		t.Parallel()

		// Should be grouping all provides and pushing them into the container
		// after applying other options. This prevents the app configuration
		// (e.g., logging) from changing halfway through our provides.

		spy := new(fxlog.Spy)
		app := fxtest.New(t, Provide(func() struct{} { return struct{}{} }),
			WithLogger(func() fxevent.Logger { return spy }))
		defer app.RequireStart().RequireStop()
		require.Equal(t,
			[]string{"Provided", "Provided", "Provided", "Provided", "LoggerInitialized", "Started"},
			spy.EventTypes())

		// Fx types get provided first to increase chance of
		// successful custom logger build.
		assert.Contains(t, spy.Events()[0].(*fxevent.Provided).OutputTypeNames, "fx.Lifecycle")
		assert.Contains(t, spy.Events()[1].(*fxevent.Provided).OutputTypeNames, "fx.Shutdowner")
		assert.Contains(t, spy.Events()[2].(*fxevent.Provided).OutputTypeNames, "fx.DotGraph")
		// Our type should be index 3.
		assert.Contains(t, spy.Events()[3].(*fxevent.Provided).OutputTypeNames, "struct {}")
	})

	t.Run("CircularGraphReturnsError", func(t *testing.T) {
		t.Parallel()

		type A struct{}
		type B struct{}
		app := NewForTest(t,
			Provide(func(A) B { return B{} }),
			Provide(func(B) A { return A{} }),
			Invoke(func(B) {}),
		)
		err := app.Err()
		require.Error(t, err, "fx.New should return an error")

		errMsg := err.Error()
		assert.Contains(t, errMsg, "cycle detected in dependency graph")
		assert.Contains(t, errMsg, "depends on func(fx_test.B) fx_test.A")
		assert.Contains(t, errMsg, "depends on func(fx_test.A) fx_test.B")
	})

	t.Run("ProvidesDotGraph", func(t *testing.T) {
		t.Parallel()

		type A struct{}
		type B struct{}
		type C struct{}
		var g DotGraph
		app := fxtest.New(t,
			Provide(func() A { return A{} }),
			Provide(func(A) B { return B{} }),
			Provide(func(A, B) C { return C{} }),
			Populate(&g),
		)
		defer app.RequireStart().RequireStop()
		require.NoError(t, app.Err())
		assert.Contains(t, g, `"fx.DotGraph" [label=<fx.DotGraph>];`)
	})

	t.Run("ProvidesWithAnnotate", func(t *testing.T) {
		t.Parallel()

		type A struct{}

		type B struct {
			In

			Foo  A   `name:"foo"`
			Bar  A   `name:"bar"`
			Foos []A `group:"foo"`
		}

		app := fxtest.New(t,
			Provide(
				Annotated{
					Target: func() A { return A{} },
					Name:   "foo",
				},
				Annotated{
					Target: func() A { return A{} },
					Name:   "bar",
				},
				Annotated{
					Target: func() A { return A{} },
					Group:  "foo",
				},
			),
			Invoke(
				func(b B) {
					assert.NotNil(t, b.Foo)
					assert.NotNil(t, b.Bar)
					assert.Len(t, b.Foos, 1)
				},
			),
		)

		defer app.RequireStart().RequireStop()
		require.NoError(t, app.Err())
	})

	t.Run("ProvidesWithAnnotateFlattened", func(t *testing.T) {
		t.Parallel()

		app := fxtest.New(t,
			Provide(Annotated{
				Target: func() []int { return []int{1} },
				Group:  "foo,flatten",
			}),
			Invoke(
				func(b struct {
					In
					Foos []int `group:"foo"`
				},
				) {
					assert.Len(t, b.Foos, 1)
				},
			),
		)

		defer app.RequireStart().RequireStop()
		require.NoError(t, app.Err())
	})

	t.Run("ProvidesWithEmptyAnnotate", func(t *testing.T) {
		t.Parallel()

		type A struct{}

		type B struct {
			In

			Foo A
		}

		app := fxtest.New(t,
			Provide(
				Annotated{
					Target: func() A { return A{} },
				},
			),
			Invoke(
				func(b B) {
					assert.NotNil(t, b.Foo)
				},
			),
		)

		defer app.RequireStart().RequireStop()
		require.NoError(t, app.Err())
	})

	t.Run("CannotNameAndGroup", func(t *testing.T) {
		t.Parallel()

		type A struct{}

		app := NewForTest(t,
			Provide(
				Annotated{
					Target: func() A { return A{} },
					Name:   "foo",
					Group:  "bar",
				},
			),
		)

		err := app.Err()
		require.Error(t, err)

		// 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:
		// go.uber.org/fx_test.TestAnnotatedWithGroupAndName
		//         /.../fx/annotated_test.go:164
		// testing.tRunner
		//         /.../go/1.13.3/libexec/src/testing/testing.go:909
		assert.Contains(t, err.Error(), "fx.Annotated may specify only one of Name or Group:")
		assert.Contains(t, err.Error(), `received fx.Annotated{Name: "foo", Group: "bar", Target: go.uber.org/fx_test.TestNewApp`)
		assert.Contains(t, err.Error(), "go.uber.org/fx_test.TestNewApp")
		assert.Contains(t, err.Error(), "/app_test.go")
	})

	t.Run("ErrorProvidingAnnotated", func(t *testing.T) {
		t.Parallel()

		app := NewForTest(t, Provide(Annotated{
			Target: 42, // not a constructor
			Name:   "foo",
		}))

		err := app.Err()
		require.Error(t, err)

		// Example:
		// fx.Provide(fx.Annotated{...}) from:
		//     go.uber.org/fx_test.TestNewApp.func8
		//         /.../fx/app_test.go:206
		//     testing.tRunner
		//         /.../go/1.13.3/libexec/src/testing/testing.go:909
		//     Failed: must provide constructor function, got 42 (type int)
		assert.Contains(t, err.Error(), `fx.Provide(fx.Annotated{Name: "foo", Target: 42}) from:`)
		assert.Contains(t, err.Error(), "go.uber.org/fx_test.TestNewApp")
		assert.Contains(t, err.Error(), "/app_test.go")
		assert.Contains(t, err.Error(), "Failed: must provide constructor function")
	})

	t.Run("ErrorProvidingAnnotate", func(t *testing.T) {
		t.Parallel()

		type t1 struct{}
		newT1 := func() t1 { return t1{} }

		// Provide twice.
		app := NewForTest(t, Provide(
			Annotate(newT1, ResultTags(`name:"foo"`)),
			Annotate(newT1, ResultTags(`name:"foo"`)),
		))

		err := app.Err()
		require.Error(t, err)

		// Example:
		// fx.Provide(fx.Annotate(go.uber.org/fx_test.TestNewApp.func10.1(), fx.ResultTags(["name:\"foo\""])) from:
		//     go.uber.org/fx_test.TestNewApp.func10
		//         /.../fx/app_test.go:305
		//     testing.tRunner
		//         /.../src/testing/testing.go:1259
		//     Failed: cannot provide function "reflect".makeFuncStub (/.../reflect/asm_amd64.s:30):
		//     cannot provide fx_test.t1[name="foo"] from [0].Field0:
		//     already provided by "reflect".makeFuncStub (/.../reflect/asm_amd64.s:30)
		assert.Contains(t, err.Error(), `fx.Provide(fx.Annotate(`)
		assert.Contains(t, err.Error(), `fx.ResultTags(["name:\"foo\""])`)
		assert.Contains(t, err.Error(), "already provided")
	})

	t.Run("ErrorProviding", func(t *testing.T) {
		t.Parallel()

		err := NewForTest(t, Provide(42)).Err()
		require.Error(t, err)

		// Example:
		// fx.Provide(..) from:
		//     go.uber.org/fx_test.TestNewApp.func8
		//         /.../fx/app_test.go:206
		//     testing.tRunner
		//         /.../go/1.13.3/libexec/src/testing/testing.go:909
		//     Failed: must provide constructor function, got 42 (type int)
		assert.Contains(t, err.Error(), "fx.Provide(42) from:")
		assert.Contains(t, err.Error(), "go.uber.org/fx_test.TestNewApp")
		assert.Contains(t, err.Error(), "/app_test.go")
		assert.Contains(t, err.Error(), "Failed: must provide constructor function")
	})

	t.Run("Decorates", func(t *testing.T) {
		t.Parallel()
		spy := new(fxlog.Spy)

		type A struct{ value int }
		app := fxtest.New(t,
			Provide(func() A { return A{value: 0} }),
			Decorate(func(a A) A { return A{value: a.value + 1} }),
			Invoke(func(a A) { assert.Equal(t, a.value, 1) }),
			WithLogger(func() fxevent.Logger { return spy }))
		defer app.RequireStart().RequireStop()

		require.Equal(t,
			[]string{"Provided", "Provided", "Provided", "Provided", "Decorated", "LoggerInitialized", "Invoking", "BeforeRun", "Run", "BeforeRun", "Run", "Invoked", "Started"},
			spy.EventTypes())
	})

	t.Run("DecoratesFromManyModules", func(t *testing.T) {
		t.Parallel()
		spy := new(fxlog.Spy)

		type A struct{ value int }
		m := Module("decorator",
			Decorate(func(a A) A { return A{value: a.value + 1} }),
		)
		app := fxtest.New(t,
			m,
			Provide(func() A { return A{value: 0} }),
			Decorate(func(a A) A { return A{value: a.value + 1} }),
			WithLogger(func() fxevent.Logger { return spy }),
		)
		defer app.RequireStart().RequireStop()

		require.Equal(t,
			[]string{"Provided", "Provided", "Provided", "Provided", "Decorated", "Decorated", "LoggerInitialized", "Started"},
			spy.EventTypes())
	})
}

// TestPrivate tests Private when used with both fx.Provide and fx.Supply.
func TestPrivate(t *testing.T) {
	t.Parallel()

	testCases := []struct {
		desc string

		// provide is either a Supply or Provide wrapper around the given int
		// that allows us to generalize these test cases for both APIs.
		provide func(int, bool) Option
	}{
		{
			desc: "Provide",
			provide: func(i int, private bool) Option {
				opts := []any{func() int { return i }}
				if private {
					opts = append(opts, Private)
				}
				return Provide(opts...)
			},
		},
		{
			desc: "Supply",
			provide: func(i int, private bool) Option {
				opts := []any{i}
				if private {
					opts = append(opts, Private)
				}
				return Supply(opts...)
			},
		},
	}

	for _, tt := range testCases {
		t.Run(tt.desc, func(t *testing.T) {
			t.Parallel()

			t.Run("CanUsePrivateFromParentModule", func(t *testing.T) {
				t.Parallel()

				var invoked bool
				app := fxtest.New(t,
					Module("SubModule", Invoke(func(a int, b string) {
						assert.Equal(t, 0, a)
						invoked = true
					})),
					Provide(func() string { return "" }),
					tt.provide(0, true /* private */),
				)
				app.RequireStart().RequireStop()
				assert.True(t, invoked)
			})

			t.Run("CannotUsePrivateFromSubModule", func(t *testing.T) {
				t.Parallel()

				app := NewForTest(t,
					Module("SubModule", tt.provide(0, true /* private */)),
					Invoke(func(a int) {}),
				)
				err := app.Err()
				require.Error(t, err)
				assert.Contains(t, err.Error(), "missing dependencies for function")
				assert.Contains(t, err.Error(), "missing type: int")
			})

			t.Run("MultipleModulesSameType", func(t *testing.T) {
				t.Parallel()

				var invoked int
				app := fxtest.New(t,
					Module("SubModuleA",
						tt.provide(1, true /* private */),
						Invoke(func(s int) {
							assert.Equal(t, 1, s)
							invoked++
						}),
					),
					Module("SubModuleB",
						tt.provide(2, true /* private */),
						Invoke(func(s int) {
							assert.Equal(t, 2, s)
							invoked++
						}),
					),
					tt.provide(3, false /* private */),
					Invoke(func(s int) {
						assert.Equal(t, 3, s)
						invoked++
					}),
				)
				app.RequireStart().RequireStop()
				assert.Equal(t, 3, invoked)
			})
		})
	}
}

func TestPrivateProvideWithDecorators(t *testing.T) {
	t.Parallel()

	testCases := []struct {
		desc string

		// provide is either a Supply or Provide wrapper around the given int
		// that allows us to generalize these test cases for both APIs.
		provide func(int) Option
		private bool
	}{
		{
			desc: "Private/Provide",
			provide: func(i int) Option {
				return Provide(
					func() int { return i },
					Private,
				)
			},
			private: true,
		},
		{
			desc: "Private/Supply",
			provide: func(i int) Option {
				return Supply(i, Private)
			},
			private: true,
		},
		{
			desc: "Public/Provide",
			provide: 
Download .txt
gitextract_i617t6c0/

├── .codecov.yml
├── .github/
│   ├── ISSUE_TEMPLATE/
│   │   ├── bug_report.md
│   │   ├── config.yml
│   │   └── feature_request.md
│   ├── dependabot.yml
│   └── workflows/
│       ├── docs.yml
│       ├── fossa.yaml
│       └── go.yml
├── .gitignore
├── .golangci.yml
├── CHANGELOG.md
├── CONTRIBUTING.md
├── LICENSE
├── Makefile
├── README.md
├── annotated.go
├── annotated_test.go
├── app.go
├── app_internal_test.go
├── app_test.go
├── app_unixes.go
├── app_wasm.go
├── app_windows.go
├── app_windows_test.go
├── broadcast.go
├── decorate.go
├── decorate_test.go
├── doc.go
├── docs/
│   ├── .gitattributes
│   ├── .gitignore
│   ├── Makefile
│   ├── ex/
│   │   ├── annotate/
│   │   │   ├── cast.go
│   │   │   ├── cast_bad.go
│   │   │   ├── cast_test.go
│   │   │   ├── github/
│   │   │   │   └── stub.go
│   │   │   ├── sample.go
│   │   │   └── sample_test.go
│   │   ├── get-started/
│   │   │   ├── 01-minimal/
│   │   │   │   ├── main.go
│   │   │   │   └── main_test.go
│   │   │   ├── 02-http-server/
│   │   │   │   ├── main.go
│   │   │   │   └── main_test.go
│   │   │   ├── 03-echo-handler/
│   │   │   │   ├── main.go
│   │   │   │   └── main_test.go
│   │   │   ├── 04-logger/
│   │   │   │   ├── main.go
│   │   │   │   └── main_test.go
│   │   │   ├── 05-registration/
│   │   │   │   ├── main.go
│   │   │   │   └── main_test.go
│   │   │   ├── 06-another-handler/
│   │   │   │   ├── main.go
│   │   │   │   └── main_test.go
│   │   │   └── 07-many-handlers/
│   │   │       ├── main.go
│   │   │       └── main_test.go
│   │   ├── modules/
│   │   │   ├── module.go
│   │   │   └── module_test.go
│   │   ├── parameter-objects/
│   │   │   ├── define.go
│   │   │   ├── define_test.go
│   │   │   ├── extend.go
│   │   │   └── extend_test.go
│   │   ├── result-objects/
│   │   │   ├── define.go
│   │   │   ├── define_test.go
│   │   │   ├── extend.go
│   │   │   └── extend_test.go
│   │   └── value-groups/
│   │       ├── consume/
│   │       │   ├── annotate.go
│   │       │   ├── consume_test.go
│   │       │   └── param.go
│   │       └── feed/
│   │           ├── annotate.go
│   │           ├── feed_test.go
│   │           └── result.go
│   ├── go.mod
│   ├── go.sum
│   ├── internal/
│   │   ├── apptest/
│   │   │   ├── run.go
│   │   │   └── run_test.go
│   │   ├── exectest/
│   │   │   ├── cmd.go
│   │   │   ├── cmd_test.go
│   │   │   ├── output.go
│   │   │   └── output_test.go
│   │   ├── httptest/
│   │   │   ├── http.go
│   │   │   └── http_test.go
│   │   ├── iotest/
│   │   │   ├── read.go
│   │   │   └── read_test.go
│   │   └── test/
│   │       ├── fake.go
│   │       ├── fake_test.go
│   │       ├── t.go
│   │       └── t_test.go
│   ├── mkdocs.yml
│   ├── pyproject.toml
│   └── src/
│       ├── annotate.md
│       ├── container.md
│       ├── faq.md
│       ├── get-started/
│       │   ├── another-handler.md
│       │   ├── conclusion.md
│       │   ├── echo-handler.md
│       │   ├── http-server.md
│       │   ├── index.md
│       │   ├── logger.md
│       │   ├── many-handlers.md
│       │   ├── minimal.md
│       │   └── registration.md
│       ├── index.md
│       ├── lifecycle.md
│       ├── modules.md
│       ├── parameter-objects.md
│       ├── result-objects.md
│       └── value-groups/
│           ├── consume.md
│           ├── feed.md
│           └── index.md
├── error_example_test.go
├── example_test.go
├── extract.go
├── extract_test.go
├── fxevent/
│   ├── console.go
│   ├── console_test.go
│   ├── doc.go
│   ├── event.go
│   ├── event_test.go
│   ├── logger.go
│   ├── slog.go
│   ├── slog_test.go
│   ├── zap.go
│   └── zap_test.go
├── fxtest/
│   ├── app.go
│   ├── app_test.go
│   ├── doc.go
│   ├── lifecycle.go
│   ├── lifecycle_test.go
│   ├── printer.go
│   ├── printer_test.go
│   ├── tb.go
│   └── tb_test.go
├── go.mod
├── go.sum
├── inout.go
├── inout_test.go
├── internal/
│   ├── e2e/
│   │   ├── README.md
│   │   ├── go.mod
│   │   ├── go.sum
│   │   ├── shutdowner_run_exitcode/
│   │   │   ├── main.go
│   │   │   └── main_test.go
│   │   └── shutdowner_wait_exitcode/
│   │       ├── main.go
│   │       └── main_test.go
│   ├── fxclock/
│   │   ├── clock.go
│   │   └── clock_test.go
│   ├── fxlog/
│   │   ├── default.go
│   │   ├── default_test.go
│   │   ├── foovendor/
│   │   │   └── foovendor.go
│   │   ├── sample.git/
│   │   │   └── sample.go
│   │   ├── spy.go
│   │   └── spy_test.go
│   ├── fxreflect/
│   │   ├── fxreflect.go
│   │   ├── fxreflect_test.go
│   │   ├── stack.go
│   │   └── stack_test.go
│   ├── leaky_test/
│   │   └── leaky_test.go
│   ├── lifecycle/
│   │   ├── lifecycle.go
│   │   └── lifecycle_test.go
│   └── testutil/
│       ├── writer.go
│       └── writer_test.go
├── invoke.go
├── lifecycle.go
├── log.go
├── log_test.go
├── module.go
├── module_test.go
├── populate.go
├── populate_example_test.go
├── populate_test.go
├── printer_writer.go
├── provide.go
├── replace.go
├── replace_test.go
├── shutdown.go
├── shutdown_test.go
├── signal.go
├── signal_test.go
├── supply.go
├── supply_test.go
├── tools/
│   ├── analysis/
│   │   └── passes/
│   │       └── allfxevents/
│   │           ├── analysis.go
│   │           ├── analysis_test.go
│   │           └── testdata/
│   │               └── src/
│   │                   ├── a/
│   │                   │   ├── full.go
│   │                   │   ├── nop.go
│   │                   │   ├── not_a_logger.go
│   │                   │   ├── partial_test.go
│   │                   │   ├── ptr.go
│   │                   │   └── value.go
│   │                   ├── b/
│   │                   │   ├── fxevent/
│   │                   │   │   └── logger.go
│   │                   │   └── not_real_fxevent.go
│   │                   └── go.uber.org/
│   │                       └── fx/
│   │                           └── fxevent/
│   │                               ├── fxevent.go
│   │                               └── partial.go
│   ├── cmd/
│   │   └── fxlint/
│   │       └── main.go
│   ├── go.mod
│   └── go.sum
└── version.go
Download .txt
SYMBOL INDEX (771 symbols across 137 files)

FILE: annotated.go
  type Annotated (line 63) | type Annotated struct
    method String (line 85) | func (a Annotated) String() string {
  type Annotation (line 117) | type Annotation interface
  type annotationError (line 131) | type annotationError struct
    method Error (line 136) | func (e *annotationError) Error() string {
    method Unwrap (line 141) | func (e *annotationError) Unwrap() error {
  type paramTagsAnnotation (line 145) | type paramTagsAnnotation struct
    method apply (line 236) | func (pt paramTagsAnnotation) apply(ann *annotated) error {
    method build (line 250) | func (pt paramTagsAnnotation) build(ann *annotated) (any, error) {
    method parameters (line 266) | func (pt paramTagsAnnotation) parameters(ann *annotated) (
  function verifyTagsSpaceSeparated (line 159) | func verifyTagsSpaceSeparated(tagIdx int, tag string) error {
  function verifyValueQuote (line 167) | func verifyValueQuote(value string) (string, error) {
  function verifyAnnotateTag (line 190) | func verifyAnnotateTag(tag string) error {
  function ParamTags (line 352) | func ParamTags(tags ...string) Annotation {
  type resultTagsAnnotation (line 356) | type resultTagsAnnotation struct
    method apply (line 379) | func (rt resultTagsAnnotation) apply(ann *annotated) error {
    method build (line 393) | func (rt resultTagsAnnotation) build(ann *annotated) (any, error) {
    method results (line 408) | func (rt resultTagsAnnotation) results(ann *annotated) (
  function ResultTags (line 542) | func ResultTags(tags ...string) Annotation {
  type outStructInfo (line 546) | type outStructInfo struct
  type _lifecycleHookAnnotationType (line 551) | type _lifecycleHookAnnotationType
  constant _unknownHookType (line 554) | _unknownHookType _lifecycleHookAnnotationType = iota
  constant _onStartHookType (line 555) | _onStartHookType
  constant _onStopHookType (line 556) | _onStopHookType
  type lifecycleHookAnnotation (line 559) | type lifecycleHookAnnotation struct
    method String (line 566) | func (la *lifecycleHookAnnotation) String() string {
    method apply (line 577) | func (la *lifecycleHookAnnotation) apply(ann *annotated) error {
    method build (line 628) | func (la *lifecycleHookAnnotation) build(ann *annotated) (any, error) {
    method validateHookDeps (line 669) | func (la *lifecycleHookAnnotation) validateHookDeps(hookParamTypes []r...
    method buildHookInstaller (line 750) | func (la *lifecycleHookAnnotation) buildHookInstaller(ann *annotated) (
    method buildHook (line 1048) | func (la *lifecycleHookAnnotation) buildHook(fn func(context.Context) ...
  function makeHookScopeCtor (line 890) | func makeHookScopeCtor(paramTypes []reflect.Type, resultTypes []reflect....
  function injectLifecycle (line 995) | func injectLifecycle(paramTypes []reflect.Type) ([]reflect.Type, func([]...
  function lifecycleExists (line 1032) | func lifecycleExists(paramTypes []reflect.Type) bool {
  function OnStart (line 1115) | func OnStart(onStart any) Annotation {
  function OnStop (line 1179) | func OnStop(onStop any) Annotation {
  type asAnnotation (line 1186) | type asAnnotation struct
    method apply (line 1294) | func (at *asAnnotation) apply(ann *annotated) error {
    method build (line 1314) | func (at *asAnnotation) build(ann *annotated) (any, error) {
    method results (line 1331) | func (at *asAnnotation) results(ann *annotated) (
  type asType (line 1191) | type asType struct
    method String (line 1196) | func (a asType) String() string {
  function isOut (line 1203) | func isOut(t reflect.Type) bool {
  function isIn (line 1208) | func isIn(t reflect.Type) bool {
  function As (line 1259) | func As(interfaces ...any) Annotation {
  function Self (line 1288) | func Self() any {
  type self (line 1292) | type self struct
  function extractResultFields (line 1407) | func extractResultFields(types []reflect.Type) ([]reflect.StructField, f...
  type fromAnnotation (line 1432) | type fromAnnotation struct
    method apply (line 1490) | func (fr *fromAnnotation) apply(ann *annotated) error {
    method build (line 1511) | func (fr *fromAnnotation) build(ann *annotated) (any, error) {
    method parameters (line 1530) | func (fr *fromAnnotation) parameters(ann *annotated) (
  function From (line 1486) | func From(interfaces ...any) Annotation {
  type annotated (line 1615) | type annotated struct
    method String (line 1629) | func (ann annotated) String() string {
    method Build (line 1650) | func (ann *annotated) Build() (any, error) {
    method applyOptionalTag (line 1693) | func (ann *annotated) applyOptionalTag() {
    method cleanUpAsResults (line 1732) | func (ann *annotated) cleanUpAsResults() {
    method typeCheckOrigFn (line 1758) | func (ann *annotated) typeCheckOrigFn() error {
    method currentResultTypes (line 1794) | func (ann *annotated) currentResultTypes() (resultTypes []reflect.Type...
    method currentParamTypes (line 1808) | func (ann *annotated) currentParamTypes() []reflect.Type {
  function Annotate (line 1901) | func Annotate(t any, anns ...Annotation) any {

FILE: annotated_test.go
  function TestAnnotated (line 41) | func TestAnnotated(t *testing.T) {
  type fromStringer (line 74) | type fromStringer struct
    method String (line 78) | func (from *fromStringer) String() string {
  type asStringer (line 82) | type asStringer struct
    method String (line 86) | func (as *asStringer) String() string {
  type anotherStringer (line 90) | type anotherStringer struct
    method String (line 94) | func (a anotherStringer) String() string {
  function TestAnnotatedFrom (line 98) | func TestAnnotatedFrom(t *testing.T) {
  function TestAnnotatedFromFailures (line 293) | func TestAnnotatedFromFailures(t *testing.T) {
  function TestAnnotatedAs (line 427) | func TestAnnotatedAs(t *testing.T) {
  function TestAnnotatedAsFailures (line 823) | func TestAnnotatedAsFailures(t *testing.T) {
  function TestAnnotatedWrongUsage (line 911) | func TestAnnotatedWrongUsage(t *testing.T) {
  function TestAnnotatedString (line 992) | func TestAnnotatedString(t *testing.T) {
  function TestAnnotate (line 1051) | func TestAnnotate(t *testing.T) {
  function TestAnnotateApplyFail (line 1675) | func TestAnnotateApplyFail(t *testing.T) {
  function TestAnnotateApplySuccess (line 1754) | func TestAnnotateApplySuccess(t *testing.T) {
  function assertApp (line 1800) | func assertApp(
  function TestHookAnnotations (line 1830) | func TestHookAnnotations(t *testing.T) {
  function TestHookAnnotationFailures (line 2276) | func TestHookAnnotationFailures(t *testing.T) {
  function TestHookAnnotationFunctionFlexibility (line 2474) | func TestHookAnnotationFunctionFlexibility(t *testing.T) {

FILE: app.go
  constant DefaultTimeout (line 45) | DefaultTimeout = 15 * time.Second
  type Option (line 56) | type Option interface
  function Error (line 68) | func Error(errs ...error) Option {
  type errorOption (line 72) | type errorOption
    method apply (line 74) | func (errs errorOption) apply(mod *module) {
    method String (line 78) | func (errs errorOption) String() string {
  function Options (line 93) | func Options(opts ...Option) Option {
  type optionGroup (line 97) | type optionGroup
    method apply (line 99) | func (og optionGroup) apply(mod *module) {
    method String (line 105) | func (og optionGroup) String() string {
  function StartTimeout (line 118) | func StartTimeout(v time.Duration) Option {
  type startTimeoutOption (line 122) | type startTimeoutOption
    method apply (line 124) | func (t startTimeoutOption) apply(m *module) {
    method String (line 133) | func (t startTimeoutOption) String() string {
  function StopTimeout (line 142) | func StopTimeout(v time.Duration) Option {
  type stopTimeoutOption (line 146) | type stopTimeoutOption
    method apply (line 148) | func (t stopTimeoutOption) apply(m *module) {
    method String (line 157) | func (t stopTimeoutOption) String() string {
  function RecoverFromPanics (line 164) | func RecoverFromPanics() Option {
  type recoverFromPanicsOption (line 168) | type recoverFromPanicsOption struct
    method apply (line 170) | func (o recoverFromPanicsOption) apply(m *module) {
    method String (line 179) | func (o recoverFromPanicsOption) String() string {
  function WithLogger (line 204) | func WithLogger(constructor any) Option {
  type withLoggerOption (line 211) | type withLoggerOption struct
    method apply (line 216) | func (l withLoggerOption) apply(m *module) {
    method String (line 223) | func (l withLoggerOption) String() string {
  type Printer (line 232) | type Printer interface
  function Logger (line 239) | func Logger(p Printer) Option {
  type loggerOption (line 243) | type loggerOption struct
    method apply (line 245) | func (l loggerOption) apply(m *module) {
    method String (line 255) | func (l loggerOption) String() string {
  type App (line 300) | type App struct
    method log (line 524) | func (app *App) log() fxevent.Logger {
    method exit (line 568) | func (app *App) exit(code int) {
    method Run (line 596) | func (app *App) Run() {
    method run (line 605) | func (app *App) run(done func() <-chan ShutdownSignal) (exitCode int) {
    method Err (line 634) | func (app *App) Err() error {
    method Start (line 660) | func (app *App) Start(ctx context.Context) (err error) {
    method withRollback (line 680) | func (app *App) withRollback(
    method start (line 700) | func (app *App) start(ctx context.Context) error {
    method Stop (line 716) | func (app *App) Stop(ctx context.Context) (err error) {
    method Done (line 741) | func (app *App) Done() <-chan os.Signal {
    method Wait (line 752) | func (app *App) Wait() <-chan ShutdownSignal {
    method StartTimeout (line 760) | func (app *App) StartTimeout() time.Duration {
    method StopTimeout (line 767) | func (app *App) StopTimeout() time.Duration {
    method dotGraph (line 771) | func (app *App) dotGraph() (DotGraph, error) {
  type provide (line 324) | type provide struct
  type invoke (line 340) | type invoke struct
  type ErrorHandler (line 353) | type ErrorHandler interface
  function ErrorHook (line 360) | func ErrorHook(funcs ...ErrorHandler) Option {
  type errorHookOption (line 364) | type errorHookOption
    method apply (line 366) | func (eho errorHookOption) apply(m *module) {
    method String (line 370) | func (eho errorHookOption) String() string {
  type errorHandlerList (line 378) | type errorHandlerList
    method HandleError (line 380) | func (ehl errorHandlerList) HandleError(err error) {
  function validate (line 387) | func validate(validate bool) Option {
  type validateOption (line 393) | type validateOption struct
    method apply (line 397) | func (o validateOption) apply(m *module) {
    method String (line 406) | func (o validateOption) String() string {
  function ValidateApp (line 412) | func ValidateApp(opts ...Option) error {
  function New (line 422) | func New(opts ...Option) *App {
  type DotGraph (line 535) | type DotGraph
  type errWithGraph (line 537) | type errWithGraph interface
  type errorWithGraph (line 541) | type errorWithGraph struct
    method Graph (line 546) | func (err errorWithGraph) Graph() DotGraph {
    method Error (line 550) | func (err errorWithGraph) Error() string {
  function VisualizeError (line 557) | func VisualizeError(err error) (string, error) {
  type withTimeoutParams (line 777) | type withTimeoutParams struct
  function withTimeout (line 787) | func withTimeout(ctx context.Context, param *withTimeoutParams) error {
  type appLogger (line 826) | type appLogger struct
    method LogEvent (line 828) | func (l appLogger) LogEvent(ev fxevent.Event) {

FILE: app_internal_test.go
  function TestAppRun (line 39) | func TestAppRun(t *testing.T) {
  function TestValidateString (line 70) | func TestValidateString(t *testing.T) {
  function WithExit (line 80) | func WithExit(f func(int)) Option {
  type withExitOption (line 84) | type withExitOption
    method String (line 86) | func (o withExitOption) String() string {
    method apply (line 90) | func (o withExitOption) apply(m *module) {
  function WithClock (line 97) | func WithClock(clock fxclock.Clock) Option {
  type withClockOption (line 101) | type withClockOption struct
    method apply (line 103) | func (o withClockOption) apply(m *module) {
    method String (line 107) | func (o withClockOption) String() string {
  function TestAnnotationError (line 111) | func TestAnnotationError(t *testing.T) {
  function TestStartDoesNotRegisterSignals (line 124) | func TestStartDoesNotRegisterSignals(t *testing.T) {

FILE: app_test.go
  function NewForTest (line 51) | func NewForTest(tb testing.TB, opts ...Option) *App {
  function NewSpied (line 63) | func NewSpied(opts ...Option) (*App, *fxlog.Spy) {
  function validateTestApp (line 71) | func validateTestApp(tb testing.TB, opts ...Option) error {
  function TestNewApp (line 83) | func TestNewApp(t *testing.T) {
  function TestPrivate (line 395) | func TestPrivate(t *testing.T) {
  function TestPrivateProvideWithDecorators (line 492) | func TestPrivateProvideWithDecorators(t *testing.T) {
  function TestWithLoggerErrorUseDefault (line 617) | func TestWithLoggerErrorUseDefault(t *testing.T) {
  function TestWithLogger (line 662) | func TestWithLogger(t *testing.T) {
  function getInt (line 754) | func getInt() int { return 0 }
  function decorateInt (line 756) | func decorateInt(i int) int { return i }
  function getModuleB (line 766) | func getModuleB() Option {
  function TestModuleTrace (line 773) | func TestModuleTrace(t *testing.T) {
  function TestRunEventEmission (line 863) | func TestRunEventEmission(t *testing.T) {
  type customError (line 1019) | type customError struct
    method Error (line 1023) | func (e *customError) Error() string {
    method Unwrap (line 1027) | func (e *customError) Unwrap() error {
  type errHandlerFunc (line 1031) | type errHandlerFunc
    method HandleError (line 1033) | func (f errHandlerFunc) HandleError(err error) { f(err) }
  function TestInvokes (line 1035) | func TestInvokes(t *testing.T) {
  function TestError (line 1118) | func TestError(t *testing.T) {
  function TestOptions (line 1195) | func TestOptions(t *testing.T) {
  function TestTimeoutOptions (line 1283) | func TestTimeoutOptions(t *testing.T) {
  function TestAppRunTimeout (line 1324) | func TestAppRunTimeout(t *testing.T) {
  function TestAppStart (line 1456) | func TestAppStart(t *testing.T) {
  function TestAppStop (line 1928) | func TestAppStop(t *testing.T) {
  function TestValidateApp (line 1981) | func TestValidateApp(t *testing.T) {
  function TestHookConstructors (line 2073) | func TestHookConstructors(t *testing.T) {
  function TestDone (line 2342) | func TestDone(t *testing.T) {
  function TestShutdownThenWait (line 2358) | func TestShutdownThenWait(t *testing.T) {
  function TestReplaceLogger (line 2388) | func TestReplaceLogger(t *testing.T) {
  function TestNopLogger (line 2404) | func TestNopLogger(t *testing.T) {
  function TestNopLoggerOptionString (line 2411) | func TestNopLoggerOptionString(t *testing.T) {
  function TestCustomLoggerWithPrinter (line 2420) | func TestCustomLoggerWithPrinter(t *testing.T) {
  function TestCustomLoggerWithLifecycle (line 2442) | func TestCustomLoggerWithLifecycle(t *testing.T) {
  function TestCustomLoggerFailure (line 2495) | func TestCustomLoggerFailure(t *testing.T) {
  type testErrorWithGraph (line 2516) | type testErrorWithGraph struct
    method Graph (line 2520) | func (we testErrorWithGraph) Graph() DotGraph {
    method Error (line 2524) | func (we testErrorWithGraph) Error() string {
  function TestVisualizeError (line 2528) | func TestVisualizeError(t *testing.T) {
  function TestErrorHook (line 2555) | func TestErrorHook(t *testing.T) {
  function TestOptionString (line 2622) | func TestOptionString(t *testing.T) {
  function TestMain (line 2731) | func TestMain(m *testing.M) {
  type testLogger (line 2735) | type testLogger struct
    method Printf (line 2737) | func (l testLogger) Printf(s string, args ...any) {
    method LogEvent (line 2741) | func (l testLogger) LogEvent(event fxevent.Event) {
    method String (line 2745) | func (l testLogger) String() string {
  type testErrorHandler (line 2749) | type testErrorHandler struct
    method HandleError (line 2751) | func (h testErrorHandler) HandleError(err error) {
    method String (line 2755) | func (h testErrorHandler) String() string {

FILE: app_unixes.go
  constant _sigINT (line 29) | _sigINT  = unix.SIGINT
  constant _sigTERM (line 30) | _sigTERM = unix.SIGTERM

FILE: app_wasm.go
  constant _sigINT (line 29) | _sigINT  = syscall.SIGINT
  constant _sigTERM (line 30) | _sigTERM = syscall.SIGTERM

FILE: app_windows.go
  constant _sigINT (line 29) | _sigINT  = windows.SIGINT
  constant _sigTERM (line 30) | _sigTERM = windows.SIGTERM

FILE: app_windows_test.go
  function TestWindowsCtrlCHandler (line 42) | func TestWindowsCtrlCHandler(t *testing.T) {
  function TestWindowsMinimalApp (line 96) | func TestWindowsMinimalApp(t *testing.T) {

FILE: broadcast.go
  type broadcaster (line 31) | type broadcaster struct
    method reset (line 51) | func (b *broadcaster) reset() {
    method Done (line 62) | func (b *broadcaster) Done() <-chan os.Signal {
    method Wait (line 83) | func (b *broadcaster) Wait() <-chan ShutdownSignal {
    method Broadcast (line 100) | func (b *broadcaster) Broadcast(signal ShutdownSignal) error {
    method broadcast (line 123) | func (b *broadcaster) broadcast(
    method broadcastDone (line 138) | func (b *broadcaster) broadcastDone(signal ShutdownSignal) (int, int) {
    method broadcastWait (line 152) | func (b *broadcaster) broadcastWait(signal ShutdownSignal) (int, int) {
  type unsentSignalError (line 166) | type unsentSignalError struct
    method Error (line 172) | func (err *unsentSignalError) Error() string {

FILE: decorate.go
  function Decorate (line 178) | func Decorate(decorators ...any) Option {
  type decorateOption (line 185) | type decorateOption struct
    method apply (line 190) | func (o decorateOption) apply(mod *module) {
    method String (line 199) | func (o decorateOption) String() string {
  type decorator (line 208) | type decorator struct
  function runDecorator (line 220) | func runDecorator(c container, d decorator, opts ...dig.DecorateOption) ...

FILE: decorate_test.go
  function TestDecorateSuccess (line 34) | func TestDecorateSuccess(t *testing.T) {
  function TestDecorateFailure (line 350) | func TestDecorateFailure(t *testing.T) {

FILE: docs/ex/annotate/cast.go
  type HTTPClient (line 32) | type HTTPClient interface
  type Config (line 43) | type Config struct
  function NewHTTPClient (line 47) | func NewHTTPClient(Config) (*http.Client, error) {
  function NewGitHubClient (line 54) | func NewGitHubClient(client HTTPClient) *github.Client {
  function options (line 59) | func options() fx.Option {

FILE: docs/ex/annotate/cast_bad.go
  function NewGitHubClient (line 35) | func NewGitHubClient(client *http.Client) *github.Client {
  function options (line 40) | func options() fx.Option {

FILE: docs/ex/annotate/cast_test.go
  function TestCast (line 32) | func TestCast(t *testing.T) {

FILE: docs/ex/annotate/github/stub.go
  type Client (line 24) | type Client struct

FILE: docs/ex/annotate/sample.go
  function howToAnnotate (line 27) | func howToAnnotate() (before, after fx.Option) {

FILE: docs/ex/annotate/sample_test.go
  function TestHowToAnnotate (line 32) | func TestHowToAnnotate(t *testing.T) {

FILE: docs/ex/get-started/01-minimal/main.go
  function main (line 27) | func main() {

FILE: docs/ex/get-started/01-minimal/main_test.go
  function TestRun (line 29) | func TestRun(t *testing.T) {

FILE: docs/ex/get-started/02-http-server/main.go
  function main (line 33) | func main() {
  function NewHTTPServer (line 51) | func NewHTTPServer(lc fx.Lifecycle) *http.Server {

FILE: docs/ex/get-started/02-http-server/main_test.go
  function TestRun (line 32) | func TestRun(t *testing.T) {

FILE: docs/ex/get-started/03-echo-handler/main.go
  function main (line 34) | func main() {
  function NewServeMux (line 55) | func NewServeMux(echo *EchoHandler) *http.ServeMux {
  type EchoHandler (line 67) | type EchoHandler struct
    method ServeHTTP (line 75) | func (*EchoHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
  function NewEchoHandler (line 70) | func NewEchoHandler() *EchoHandler {
  function NewHTTPServer (line 86) | func NewHTTPServer(lc fx.Lifecycle, mux *http.ServeMux) *http.Server {

FILE: docs/ex/get-started/03-echo-handler/main_test.go
  function TestRun (line 31) | func TestRun(t *testing.T) {

FILE: docs/ex/get-started/04-logger/main.go
  function main (line 35) | func main() {
  function NewServeMux (line 55) | func NewServeMux(echo *EchoHandler) *http.ServeMux {
  type EchoHandler (line 64) | type EchoHandler struct
    method ServeHTTP (line 80) | func (h *EchoHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
  function NewEchoHandler (line 72) | func NewEchoHandler(log *zap.Logger) *EchoHandler {
  function NewHTTPServer (line 91) | func NewHTTPServer(lc fx.Lifecycle, mux *http.ServeMux, log *zap.Logger)...

FILE: docs/ex/get-started/04-logger/main_test.go
  function TestRun (line 31) | func TestRun(t *testing.T) {

FILE: docs/ex/get-started/05-registration/main.go
  function main (line 34) | func main() {
  type Route (line 58) | type Route interface
  function NewServeMux (line 71) | func NewServeMux(route Route) *http.ServeMux {
  type EchoHandler (line 81) | type EchoHandler struct
    method Pattern (line 93) | func (*EchoHandler) Pattern() string {
    method ServeHTTP (line 100) | func (h *EchoHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
  function NewEchoHandler (line 86) | func NewEchoHandler(log *zap.Logger) *EchoHandler {
  function NewHTTPServer (line 108) | func NewHTTPServer(lc fx.Lifecycle, mux *http.ServeMux, log *zap.Logger)...

FILE: docs/ex/get-started/05-registration/main_test.go
  function TestRun (line 31) | func TestRun(t *testing.T) {

FILE: docs/ex/get-started/06-another-handler/main.go
  function main (line 35) | func main() {
  type Route (line 74) | type Route interface
  function NewServeMux (line 85) | func NewServeMux(route1, route2 Route) *http.ServeMux {
  type HelloHandler (line 98) | type HelloHandler struct
    method Pattern (line 112) | func (*HelloHandler) Pattern() string {
    method ServeHTTP (line 120) | func (h *HelloHandler) ServeHTTP(w http.ResponseWriter, r *http.Reques...
  function NewHelloHandler (line 103) | func NewHelloHandler(log *zap.Logger) *HelloHandler {
  type EchoHandler (line 139) | type EchoHandler struct
    method Pattern (line 150) | func (*EchoHandler) Pattern() string {
    method ServeHTTP (line 155) | func (h *EchoHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
  function NewEchoHandler (line 144) | func NewEchoHandler(log *zap.Logger) *EchoHandler {
  function NewHTTPServer (line 163) | func NewHTTPServer(lc fx.Lifecycle, mux *http.ServeMux, log *zap.Logger)...

FILE: docs/ex/get-started/06-another-handler/main_test.go
  function TestRun (line 31) | func TestRun(t *testing.T) {

FILE: docs/ex/get-started/07-many-handlers/main.go
  function main (line 35) | func main() {
  function AsRoute (line 64) | func AsRoute(f any) any {
  type Route (line 76) | type Route interface
  function NewServeMux (line 86) | func NewServeMux(routes []Route) *http.ServeMux {
  type HelloHandler (line 98) | type HelloHandler struct
    method Pattern (line 109) | func (*HelloHandler) Pattern() string {
    method ServeHTTP (line 114) | func (h *HelloHandler) ServeHTTP(w http.ResponseWriter, r *http.Reques...
  function NewHelloHandler (line 103) | func NewHelloHandler(log *zap.Logger) *HelloHandler {
  type EchoHandler (line 131) | type EchoHandler struct
    method Pattern (line 142) | func (*EchoHandler) Pattern() string {
    method ServeHTTP (line 147) | func (h *EchoHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
  function NewEchoHandler (line 136) | func NewEchoHandler(log *zap.Logger) *EchoHandler {
  function NewHTTPServer (line 155) | func NewHTTPServer(lc fx.Lifecycle, mux *http.ServeMux, log *zap.Logger)...

FILE: docs/ex/get-started/07-many-handlers/main_test.go
  function TestRun (line 31) | func TestRun(t *testing.T) {

FILE: docs/ex/modules/module.go
  type Config (line 56) | type Config struct
  function parseConfig (line 62) | func parseConfig() (Config, error) {
  type Params (line 68) | type Params struct
  type Result (line 79) | type Result struct
  function New (line 89) | func New(p Params) (Result, error) {
  type Server (line 97) | type Server struct
    method Start (line 100) | func (*Server) Start() error {
  function startServer (line 104) | func startServer(srv *Server) error {
  function wrapLogger (line 108) | func wrapLogger(log *zap.Logger) *zap.Logger {

FILE: docs/ex/modules/module_test.go
  function TestModule (line 32) | func TestModule(t *testing.T) {

FILE: docs/ex/parameter-objects/define.go
  type Client (line 31) | type Client struct
  type ClientConfig (line 38) | type ClientConfig struct
  type ClientParams (line 46) | type ClientParams struct
  function NewClient (line 62) | func NewClient(p ClientParams) (*Client, error) {

FILE: docs/ex/parameter-objects/define_test.go
  function TestClientParams (line 32) | func TestClientParams(t *testing.T) {

FILE: docs/ex/parameter-objects/extend.go
  type Params (line 33) | type Params struct
  function New (line 49) | func New(p Params) (*Client, error) {

FILE: docs/ex/parameter-objects/extend_test.go
  function TestExtendParams (line 33) | func TestExtendParams(t *testing.T) {

FILE: docs/ex/result-objects/define.go
  type Client (line 26) | type Client struct
  type ClientResult (line 32) | type ClientResult struct
  function NewClient (line 47) | func NewClient() (ClientResult, error) {

FILE: docs/ex/result-objects/define_test.go
  function TestClientResult (line 31) | func TestClientResult(t *testing.T) {

FILE: docs/ex/result-objects/extend.go
  type Inspector (line 26) | type Inspector struct
  type Result (line 31) | type Result struct
  function New (line 45) | func New() (Result, error) {

FILE: docs/ex/result-objects/extend_test.go
  function TestExtendResult (line 31) | func TestExtendResult(t *testing.T) {

FILE: docs/ex/value-groups/consume/annotate.go
  type Emitter (line 51) | type Emitter struct
  function NewEmitter (line 56) | func NewEmitter(watchers []Watcher) (*Emitter, error) {
  function EmitterFrom (line 80) | func EmitterFrom(watchers ...Watcher) (*Emitter, error) {

FILE: docs/ex/value-groups/consume/consume_test.go
  function TestConsume (line 31) | func TestConsume(t *testing.T) {

FILE: docs/ex/value-groups/consume/param.go
  type Watcher (line 26) | type Watcher interface
  type Params (line 38) | type Params struct
  type Result (line 51) | type Result struct
  function New (line 60) | func New(p Params) (Result, error) {

FILE: docs/ex/value-groups/feed/annotate.go
  type FileWatcher (line 47) | type FileWatcher struct
  function NewFileWatcher (line 64) | func NewFileWatcher( /* ... */ ) (*FileWatcher, error) {
  function NewWatcher (line 73) | func NewWatcher( /* ... */ ) (Watcher, error) {

FILE: docs/ex/value-groups/feed/feed_test.go
  function TestWatcherModules (line 31) | func TestWatcherModules(t *testing.T) {

FILE: docs/ex/value-groups/feed/result.go
  type Watcher (line 33) | type Watcher interface
  type watcher (line 35) | type watcher struct
  type Result (line 40) | type Result struct
  function New (line 55) | func New( /* ... */ ) (Result, error) {

FILE: docs/internal/apptest/run.go
  type StartOption (line 37) | type StartOption interface
  type startOptions (line 39) | type startOptions struct
  type isRunningOption (line 44) | type isRunningOption
    method apply (line 54) | func (o isRunningOption) apply(opts *startOptions) {
  function IsRunning (line 50) | func IsRunning(f func(string) bool) StartOption {
  function DefaultIsRunning (line 63) | func DefaultIsRunning(line string) bool {
  type timeoutOption (line 80) | type timeoutOption
    method apply (line 90) | func (o timeoutOption) apply(opts *startOptions) {
  function Timeout (line 86) | func Timeout(t time.Duration) StartOption {
  function Start (line 98) | func Start(t test.T, main func(), options ...StartOption) {

FILE: docs/internal/apptest/run_test.go
  function waitForInterrupt (line 35) | func waitForInterrupt() {
  function TestStart_FxLogger (line 41) | func TestStart_FxLogger(t *testing.T) {
  function TestStart_ZapLogger (line 51) | func TestStart_ZapLogger(t *testing.T) {
  function TestStart_Custom (line 61) | func TestStart_Custom(t *testing.T) {
  function TestStart_UnexpectedExit (line 73) | func TestStart_UnexpectedExit(t *testing.T) {
  function TestStart_Timoeut (line 85) | func TestStart_Timoeut(t *testing.T) {

FILE: docs/internal/exectest/cmd.go
  function Command (line 42) | func Command(t test.T, main func()) *exec.Cmd {

FILE: docs/internal/exectest/cmd_test.go
  function TestCommandSuccess (line 33) | func TestCommandSuccess(t *testing.T) {
  function TestCommandNonZero (line 46) | func TestCommandNonZero(t *testing.T) {

FILE: docs/internal/exectest/output.go
  function StartWithOutput (line 39) | func StartWithOutput(t test.T, cmd *exec.Cmd) io.Reader {

FILE: docs/internal/exectest/output_test.go
  function TestStartOutputReader_Stdout (line 33) | func TestStartOutputReader_Stdout(t *testing.T) {
  function TestStartOutputReader_Stderr (line 45) | func TestStartOutputReader_Stderr(t *testing.T) {
  function TestStartOutputReader_Combined (line 57) | func TestStartOutputReader_Combined(t *testing.T) {

FILE: docs/internal/httptest/http.go
  function PostSuccess (line 38) | func PostSuccess(t test.T, url, body string) string {

FILE: docs/internal/httptest/http_test.go
  function TestPostSuccess (line 33) | func TestPostSuccess(t *testing.T) {

FILE: docs/internal/iotest/read.go
  function ReadAll (line 32) | func ReadAll(t test.T, r io.Reader) string {

FILE: docs/internal/iotest/read_test.go
  function TestReadAll (line 33) | func TestReadAll(t *testing.T) {
  type fakeT (line 46) | type fakeT struct
    method Errorf (line 52) | func (t *fakeT) Errorf(msg string, args ...any) {
    method FailNow (line 56) | func (t *fakeT) FailNow() {

FILE: docs/internal/test/fake.go
  type FakeReport (line 30) | type FakeReport struct
  function WithFake (line 40) | func WithFake(t T, f func(T)) FakeReport {
  type fakeT (line 62) | type fakeT struct
    method Errorf (line 72) | func (t *fakeT) Errorf(msg string, args ...any) {
    method FailNow (line 80) | func (t *fakeT) FailNow() {

FILE: docs/internal/test/fake_test.go
  function TestWithFake_Success (line 29) | func TestWithFake_Success(t *testing.T) {
  function TestWithFake_Failure (line 38) | func TestWithFake_Failure(t *testing.T) {
  function TestWithFake_Fatal (line 47) | func TestWithFake_Fatal(t *testing.T) {

FILE: docs/internal/test/t.go
  type T (line 24) | type T interface

FILE: error_example_test.go
  function ExampleError (line 32) | func ExampleError() {

FILE: example_test.go
  function NewLogger (line 50) | func NewLogger() *log.Logger {
  function NewHandler (line 84) | func NewHandler(logger *log.Logger) (http.Handler, error) {
  function NewMux (line 119) | func NewMux(lc fx.Lifecycle, logger *log.Logger) *http.ServeMux {
  function Register (line 180) | func Register(mux *http.ServeMux, h http.Handler) {
  function Example (line 184) | func Example() {

FILE: extract.go
  function Extract (line 37) | func Extract(target any) Option {
  function isExported (line 153) | func isExported(id string) bool {

FILE: extract_test.go
  function TestExtract (line 36) | func TestExtract(t *testing.T) {

FILE: fxevent/console.go
  type ConsoleLogger (line 33) | type ConsoleLogger struct
    method logf (line 39) | func (l *ConsoleLogger) logf(msg string, args ...any) {
    method LogEvent (line 44) | func (l *ConsoleLogger) LogEvent(event Event) {

FILE: fxevent/console_test.go
  type richError (line 37) | type richError struct
    method Error (line 39) | func (e *richError) Error() string { return "plain error" }
    method Format (line 41) | func (e *richError) Format(w fmt.State, c rune) {
  function TestConsoleLogger (line 50) | func TestConsoleLogger(t *testing.T) {
  function joinLines (line 416) | func joinLines(lines ...string) string {

FILE: fxevent/event.go
  type Event (line 29) | type Event interface
  type OnStartExecuting (line 54) | type OnStartExecuting struct
    method event (line 34) | func (*OnStartExecuting) event()  {}
  type OnStartExecuted (line 64) | type OnStartExecuted struct
    method event (line 35) | func (*OnStartExecuted) event()   {}
  type OnStopExecuting (line 84) | type OnStopExecuting struct
    method event (line 36) | func (*OnStopExecuting) event()   {}
  type OnStopExecuted (line 94) | type OnStopExecuted struct
    method event (line 37) | func (*OnStopExecuted) event()    {}
  type Supplied (line 110) | type Supplied struct
    method event (line 38) | func (*Supplied) event()          {}
  type Provided (line 128) | type Provided struct
    method event (line 39) | func (*Provided) event()          {}
  type Replaced (line 155) | type Replaced struct
    method event (line 40) | func (*Replaced) event()          {}
  type Decorated (line 173) | type Decorated struct
    method event (line 41) | func (*Decorated) event()         {}
  type BeforeRun (line 197) | type BeforeRun struct
    method event (line 42) | func (*BeforeRun) event()         {}
  type Run (line 210) | type Run struct
    method event (line 43) | func (*Run) event()               {}
  type Invoking (line 230) | type Invoking struct
    method event (line 44) | func (*Invoking) event()          {}
  type Invoked (line 240) | type Invoked struct
    method event (line 45) | func (*Invoked) event()           {}
  type Started (line 257) | type Started struct
    method event (line 50) | func (*Started) event()           {}
  type Stopping (line 265) | type Stopping struct
    method event (line 46) | func (*Stopping) event()          {}
  type Stopped (line 272) | type Stopped struct
    method event (line 47) | func (*Stopped) event()           {}
  type RollingBack (line 279) | type RollingBack struct
    method event (line 48) | func (*RollingBack) event()       {}
  type RolledBack (line 286) | type RolledBack struct
    method event (line 49) | func (*RolledBack) event()        {}
  type LoggerInitialized (line 293) | type LoggerInitialized struct
    method event (line 51) | func (*LoggerInitialized) event() {}

FILE: fxevent/event_test.go
  function TestForCoverage (line 30) | func TestForCoverage(t *testing.T) {
  function TestMain (line 59) | func TestMain(m *testing.M) {

FILE: fxevent/logger.go
  type Logger (line 24) | type Logger interface
  type nopLogger (line 32) | type nopLogger struct
    method LogEvent (line 36) | func (nopLogger) LogEvent(Event) {}
    method String (line 38) | func (nopLogger) String() string { return "NopLogger" }

FILE: fxevent/slog.go
  type SlogLogger (line 35) | type SlogLogger struct
    method UseContext (line 44) | func (l *SlogLogger) UseContext(ctx context.Context) {
    method UseLogLevel (line 49) | func (l *SlogLogger) UseLogLevel(level slog.Level) {
    method UseErrorLevel (line 54) | func (l *SlogLogger) UseErrorLevel(level slog.Level) {
    method filter (line 58) | func (l *SlogLogger) filter(fields []any) []any {
    method logEvent (line 74) | func (l *SlogLogger) logEvent(msg string, fields ...any) {
    method logError (line 78) | func (l *SlogLogger) logError(msg string, fields ...any) {
    method LogEvent (line 88) | func (l *SlogLogger) LogEvent(event Event) {
  type slogFieldSkip (line 260) | type slogFieldSkip struct
  function slogMaybeModuleField (line 262) | func slogMaybeModuleField(name string) slog.Attr {
  function slogMaybeBool (line 269) | func slogMaybeBool(name string, b bool) slog.Attr {
  function slogErr (line 276) | func slogErr(err error) slog.Attr {
  function slogStrings (line 280) | func slogStrings(key string, str []string) slog.Attr {

FILE: fxevent/slog_test.go
  type slogObservableEntry (line 39) | type slogObservableEntry struct
    method unwrap (line 43) | func (s slogObservableEntry) unwrap(attr slog.Attr, out map[string]any) {
    method ContextMap (line 60) | func (s slogObservableEntry) ContextMap() map[string]any {
  type slogObservableLogger (line 70) | type slogObservableLogger struct
    method Enabled (line 76) | func (s *slogObservableLogger) Enabled(ctx context.Context, level slog...
    method Handle (line 80) | func (s *slogObservableLogger) Handle(ctx context.Context, record slog...
    method WithAttrs (line 85) | func (s *slogObservableLogger) WithAttrs(attrs []slog.Attr) slog.Handl...
    method WithGroup (line 93) | func (s *slogObservableLogger) WithGroup(name string) slog.Handler {
    method TakeAll (line 97) | func (s *slogObservableLogger) TakeAll() []slogObservableEntry {
  function newSlogObservableLogger (line 101) | func newSlogObservableLogger(level slog.Level) (*slog.Logger, *slogObser...
  function TestSlogLogger (line 106) | func TestSlogLogger(t *testing.T) {

FILE: fxevent/zap.go
  type ZapLogger (line 31) | type ZapLogger struct
    method UseErrorLevel (line 41) | func (l *ZapLogger) UseErrorLevel(level zapcore.Level) {
    method UseLogLevel (line 46) | func (l *ZapLogger) UseLogLevel(level zapcore.Level) {
    method logEvent (line 50) | func (l *ZapLogger) logEvent(msg string, fields ...zap.Field) {
    method logError (line 54) | func (l *ZapLogger) logError(msg string, fields ...zap.Field) {
    method LogEvent (line 63) | func (l *ZapLogger) LogEvent(event Event) {
  function moduleField (line 235) | func moduleField(name string) zap.Field {
  function maybeBool (line 242) | func maybeBool(name string, b bool) zap.Field {

FILE: fxevent/zap_test.go
  function TestZapLogger (line 38) | func TestZapLogger(t *testing.T) {

FILE: fxtest/app.go
  type App (line 31) | type App struct
    method RequireStart (line 56) | func (app *App) RequireStart() *App {
    method RequireStop (line 68) | func (app *App) RequireStop() {
  function New (line 38) | func New(tb TB, opts ...fx.Option) *App {

FILE: fxtest/app_test.go
  function TestApp (line 32) | func TestApp(t *testing.T) {

FILE: fxtest/lifecycle.go
  type panicT (line 38) | type panicT struct
    method format (line 48) | func (t *panicT) format(s string, args ...any) string {
    method Logf (line 52) | func (t *panicT) Logf(s string, args ...any) {
    method Errorf (line 56) | func (t *panicT) Errorf(s string, args ...any) {
    method FailNow (line 61) | func (t *panicT) FailNow() {
  type LifecycleOption (line 71) | type LifecycleOption interface
  function EnforceTimeout (line 78) | func EnforceTimeout(enforce bool) LifecycleOption {
  type enforceTimeout (line 84) | type enforceTimeout struct
    method apply (line 88) | func (e *enforceTimeout) apply(lc *Lifecycle) {
  type Lifecycle (line 97) | type Lifecycle struct
    method withTimeout (line 125) | func (l *Lifecycle) withTimeout(ctx context.Context, fn func(context.C...
    method Start (line 151) | func (l *Lifecycle) Start(ctx context.Context) error {
    method RequireStart (line 157) | func (l *Lifecycle) RequireStart() *Lifecycle {
    method Stop (line 174) | func (l *Lifecycle) Stop(ctx context.Context) error {
    method RequireStop (line 180) | func (l *Lifecycle) RequireStop() {
    method Append (line 191) | func (l *Lifecycle) Append(h fx.Hook) {
  function NewLifecycle (line 107) | func NewLifecycle(t TB, opts ...LifecycleOption) *Lifecycle {

FILE: fxtest/lifecycle_test.go
  function TestLifecycle (line 37) | func TestLifecycle(t *testing.T) {
  function TestEnforceTimeout (line 121) | func TestEnforceTimeout(t *testing.T) {
  function TestLifecycle_OptionalT (line 217) | func TestLifecycle_OptionalT(t *testing.T) {
  function TestPanicT (line 267) | func TestPanicT(t *testing.T) {
  function TestMain (line 320) | func TestMain(m *testing.M) {

FILE: fxtest/printer.go
  function NewTestLogger (line 31) | func NewTestLogger(t TB) fxevent.Logger {
  function WithTestLogger (line 37) | func WithTestLogger(t TB) fx.Option {
  type testPrinter (line 43) | type testPrinter struct
    method Printf (line 52) | func (p *testPrinter) Printf(format string, args ...any) {
  function NewTestPrinter (line 48) | func NewTestPrinter(t TB) fx.Printer {

FILE: fxtest/printer_test.go
  function TestNewTestPrinter (line 29) | func TestNewTestPrinter(t *testing.T) {

FILE: fxtest/tb.go
  type TB (line 25) | type TB interface

FILE: fxtest/tb_test.go
  type tb (line 32) | type tb struct
    method FailNow (line 42) | func (t *tb) FailNow() {
    method Errorf (line 46) | func (t *tb) Errorf(format string, args ...any) {
    method Logf (line 51) | func (t *tb) Logf(format string, args ...any) {
  function newTB (line 38) | func newTB() *tb {

FILE: inout_test.go
  function TestIn (line 32) | func TestIn(t *testing.T) {
  function TestOut (line 41) | func TestOut(t *testing.T) {
  function TestOptionalTypes (line 50) | func TestOptionalTypes(t *testing.T) {
  function TestNamedTypes (line 93) | func TestNamedTypes(t *testing.T) {
  function TestIgnoreUnexported (line 145) | func TestIgnoreUnexported(t *testing.T) {

FILE: internal/e2e/shutdowner_run_exitcode/main.go
  function main (line 27) | func main() {

FILE: internal/e2e/shutdowner_run_exitcode/main_test.go
  function TestMain (line 36) | func TestMain(m *testing.M) {
  function TestShutdownExitCode (line 52) | func TestShutdownExitCode(t *testing.T) {

FILE: internal/e2e/shutdowner_wait_exitcode/main.go
  function main (line 32) | func main() {

FILE: internal/e2e/shutdowner_wait_exitcode/main_test.go
  function TestMain (line 36) | func TestMain(m *testing.M) {
  function TestShutdownExitCode (line 52) | func TestShutdownExitCode(t *testing.T) {

FILE: internal/fxclock/clock.go
  type Clock (line 32) | type Clock interface
  type systemClock (line 42) | type systemClock struct
    method Now (line 44) | func (systemClock) Now() time.Time {
    method Since (line 48) | func (systemClock) Since(t time.Time) time.Duration {
    method Sleep (line 52) | func (systemClock) Sleep(d time.Duration) {
    method WithTimeout (line 56) | func (systemClock) WithTimeout(ctx context.Context, d time.Duration) (...
  type Mock (line 68) | type Mock struct
    method Now (line 91) | func (c *Mock) Now() time.Time {
    method Since (line 99) | func (c *Mock) Since(t time.Time) time.Duration {
    method Sleep (line 107) | func (c *Mock) Sleep(d time.Duration) {
    method WithTimeout (line 119) | func (c *Mock) WithTimeout(ctx context.Context, d time.Duration) (cont...
    method runAt (line 180) | func (c *Mock) runAt(t time.Time, fn func()) {
    method AwaitScheduled (line 190) | func (c *Mock) AwaitScheduled(n int) {
    method Add (line 220) | func (c *Mock) Add(d time.Duration) {
  function NewMock (line 84) | func NewMock() *Mock {
  type deadlineCtx (line 145) | type deadlineCtx struct
    method Deadline (line 158) | func (c *deadlineCtx) Deadline() (deadline time.Time, ok bool) { retur...
    method Done (line 159) | func (c *deadlineCtx) Done() <-chan struct{}                   { retur...
    method Value (line 160) | func (c *deadlineCtx) Value(key any) any                       { retur...
    method Err (line 162) | func (c *deadlineCtx) Err() error {
    method cancel (line 168) | func (c *deadlineCtx) cancel(err error) {
  type waiter (line 206) | type waiter struct

FILE: internal/fxclock/clock_test.go
  function TestSystemClock (line 32) | func TestSystemClock(t *testing.T) {
  function TestMockClock (line 37) | func TestMockClock(t *testing.T) {
  function testClock (line 42) | func testClock(t *testing.T, clock Clock, advance func(d time.Duration)) {
  function TestMock_Sleep (line 124) | func TestMock_Sleep(t *testing.T) {
  function TestMock_AddNegative (line 162) | func TestMock_AddNegative(t *testing.T) {
  function TestMock_ManySleepers (line 167) | func TestMock_ManySleepers(t *testing.T) {

FILE: internal/fxlog/default.go
  function DefaultLogger (line 30) | func DefaultLogger(w io.Writer) fxevent.Logger {

FILE: internal/fxlog/default_test.go
  function TestNew (line 31) | func TestNew(t *testing.T) {
  function TestMain (line 39) | func TestMain(m *testing.M) {

FILE: internal/fxlog/foovendor/foovendor.go
  function New (line 24) | func New() string { return "foovendor" }

FILE: internal/fxlog/sample.git/sample.go
  function New (line 24) | func New() (string, error) {

FILE: internal/fxlog/spy.go
  type Events (line 31) | type Events
    method Len (line 34) | func (es Events) Len() int { return len(es) }
    method SelectByTypeName (line 38) | func (es Events) SelectByTypeName(name string) Events {
  type Spy (line 50) | type Spy struct
    method LogEvent (line 58) | func (s *Spy) LogEvent(event fxevent.Event) {
    method Events (line 65) | func (s *Spy) Events() Events {
    method EventTypes (line 75) | func (s *Spy) EventTypes() []string {
    method Reset (line 87) | func (s *Spy) Reset() {

FILE: internal/fxlog/spy_test.go
  function TestSpy (line 31) | func TestSpy(t *testing.T) {

FILE: internal/fxreflect/fxreflect.go
  function sanitize (line 38) | func sanitize(function string) string {
  function Caller (line 50) | func Caller() string {
  function FuncName (line 55) | func FuncName(fn any) string {
  function shouldIgnoreFrame (line 68) | func shouldIgnoreFrame(f Frame) bool {

FILE: internal/fxreflect/fxreflect_test.go
  function TestCaller (line 30) | func TestCaller(t *testing.T) {
  function someFunc (line 36) | func someFunc() {}
  function TestFuncName (line 38) | func TestFuncName(t *testing.T) {
  function TestSanitizeFuncNames (line 67) | func TestSanitizeFuncNames(t *testing.T) {
  function TestMain (line 100) | func TestMain(m *testing.M) {

FILE: internal/fxreflect/stack.go
  type Frame (line 31) | type Frame struct
    method String (line 44) | func (f Frame) String() string {
  constant _defaultCallersDepth (line 72) | _defaultCallersDepth = 8
  type Stack (line 86) | type Stack
    method String (line 91) | func (fs Stack) String() string {
    method Strings (line 99) | func (fs Stack) Strings() []string {
    method Format (line 108) | func (fs Stack) Format(w fmt.State, c rune) {
    method CallerName (line 123) | func (fs Stack) CallerName() string {
  function CallerStack (line 137) | func CallerStack(skip, depth int) Stack {

FILE: internal/fxreflect/stack_test.go
  function TestStack (line 31) | func TestStack(t *testing.T) {
  function TestDeepStack (line 68) | func TestDeepStack(t *testing.T) {
  function TestStackCallerName (line 87) | func TestStackCallerName(t *testing.T) {
  function TestFrameString (line 169) | func TestFrameString(t *testing.T) {
  function TestStackFormat (line 223) | func TestStackFormat(t *testing.T) {

FILE: internal/leaky_test/leaky_test.go
  function TestRecoverFromPanicsOption (line 35) | func TestRecoverFromPanicsOption(t *testing.T) {

FILE: internal/lifecycle/lifecycle.go
  type Callable (line 67) | type Callable interface
  function Wrap (line 72) | func Wrap[T Callable](x T) (ContextErrorFunc, string) {
  type Hook (line 117) | type Hook struct
  type appState (line 126) | type appState
    method String (line 136) | func (as appState) String() string {
  constant stopped (line 129) | stopped appState = iota
  constant starting (line 130) | starting
  constant incompleteStart (line 131) | incompleteStart
  constant started (line 132) | started
  constant stopping (line 133) | stopping
  type Lifecycle (line 154) | type Lifecycle struct
    method Append (line 172) | func (l *Lifecycle) Append(hook Hook) {
    method Start (line 182) | func (l *Lifecycle) Start(ctx context.Context) error {
    method runStartHook (line 236) | func (l *Lifecycle) runStartHook(ctx context.Context, hook Hook) (runt...
    method Stop (line 262) | func (l *Lifecycle) Stop(ctx context.Context) error {
    method runStopHook (line 321) | func (l *Lifecycle) runStopHook(ctx context.Context, hook Hook) (runti...
    method RunningHookCaller (line 347) | func (l *Lifecycle) RunningHookCaller() string {
  function New (line 167) | func New(logger fxevent.Logger, clock fxclock.Clock) *Lifecycle {
  type HookRecord (line 354) | type HookRecord struct
  type HookRecords (line 361) | type HookRecords
    method Len (line 363) | func (rs HookRecords) Len() int {
    method Less (line 367) | func (rs HookRecords) Less(i, j int) bool {
    method Swap (line 372) | func (rs HookRecords) Swap(i, j int) {
    method String (line 377) | func (rs HookRecords) String() string {
    method Format (line 387) | func (rs HookRecords) Format(w fmt.State, c rune) {

FILE: internal/lifecycle/lifecycle_test.go
  function testLogger (line 43) | func testLogger(t *testing.T) fxevent.Logger {
  function TestLifecycleStart (line 47) | func TestLifecycleStart(t *testing.T) {
  function TestLifecycleStop (line 161) | func TestLifecycleStop(t *testing.T) {
  function TestHookRecordsFormat (line 333) | func TestHookRecordsFormat(t *testing.T) {
  function TestMain (line 378) | func TestMain(m *testing.M) {

FILE: internal/testutil/writer.go
  type TestingT (line 28) | type TestingT interface
  type WriteSyncer (line 34) | type WriteSyncer struct
    method Write (line 39) | func (w WriteSyncer) Write(bs []byte) (int, error) {
    method Sync (line 45) | func (WriteSyncer) Sync() error { return nil }

FILE: internal/testutil/writer_test.go
  type spy (line 32) | type spy struct
    method Clear (line 34) | func (s *spy) Clear() { s.Logs = nil }
    method Logf (line 36) | func (s *spy) Logf(msg string, args ...any) {
  function TestWriteSyncer (line 40) | func TestWriteSyncer(t *testing.T) {

FILE: invoke.go
  function Invoke (line 64) | func Invoke(funcs ...any) Option {
  type invokeOption (line 71) | type invokeOption struct
    method apply (line 76) | func (o invokeOption) apply(mod *module) {
    method String (line 85) | func (o invokeOption) String() string {
  function runInvoke (line 93) | func runInvoke(c container, i invoke) error {

FILE: lifecycle.go
  type HookFunc (line 30) | type HookFunc interface
  type Lifecycle (line 37) | type Lifecycle interface
  type Hook (line 45) | type Hook struct
  function StartHook (line 77) | func StartHook[T HookFunc](start T) Hook {
  function StopHook (line 110) | func StopHook[T HookFunc](stop T) Hook {
  function StartStopHook (line 122) | func StartStopHook[T1 HookFunc, T2 HookFunc](start T1, stop T2) Hook {
  type lifecycleWrapper (line 136) | type lifecycleWrapper struct
    method Append (line 140) | func (l *lifecycleWrapper) Append(h Hook) {

FILE: log.go
  type logBuffer (line 29) | type logBuffer struct
    method LogEvent (line 35) | func (l *logBuffer) LogEvent(event fxevent.Event) {
    method Connect (line 44) | func (l *logBuffer) Connect(logger fxevent.Logger) {

FILE: log_test.go
  function TestLogBufferConnect (line 34) | func TestLogBufferConnect(t *testing.T) {
  function TestLogBufferLog (line 48) | func TestLogBufferLog(t *testing.T) {
  function TestWithLoggerDecorate (line 64) | func TestWithLoggerDecorate(t *testing.T) {

FILE: module.go
  type container (line 37) | type container interface
  function Module (line 78) | func Module(name string, opts ...Option) Option {
  type moduleOption (line 87) | type moduleOption struct
    method String (line 93) | func (o moduleOption) String() string {
    method apply (line 97) | func (o moduleOption) apply(mod *module) {
  type module (line 120) | type module struct
    method build (line 150) | func (m *module) build(app *App, root *dig.Container) {
    method provideAll (line 173) | func (m *module) provideAll() {
    method provide (line 183) | func (m *module) provide(p provide) {
    method supply (line 235) | func (m *module) supply(p provide) {
    method installAllEventLoggers (line 270) | func (m *module) installAllEventLoggers() {
    method installEventLogger (line 290) | func (m *module) installEventLogger(buffer *logBuffer) (err error) {
    method invokeAll (line 313) | func (m *module) invokeAll() error {
    method invoke (line 329) | func (m *module) invoke(i invoke) (err error) {
    method decorateAll (line 345) | func (m *module) decorateAll() error {
    method decorate (line 360) | func (m *module) decorate(d decorator) (err error) {
    method replace (line 405) | func (m *module) replace(d decorator) error {
  type scope (line 138) | type scope interface

FILE: module_test.go
  function TestModuleSuccess (line 39) | func TestModuleSuccess(t *testing.T) {
  function TestModuleFailures (line 437) | func TestModuleFailures(t *testing.T) {

FILE: populate.go
  function Populate (line 64) | func Populate(targets ...any) Option {

FILE: populate_example_test.go
  function ExamplePopulate (line 30) | func ExamplePopulate() {

FILE: populate_test.go
  function TestPopulate (line 34) | func TestPopulate(t *testing.T) {
  function TestPopulateErrors (line 263) | func TestPopulateErrors(t *testing.T) {
  function TestPopulateValidateApp (line 332) | func TestPopulateValidateApp(t *testing.T) {

FILE: printer_writer.go
  type printerWriter (line 25) | type printerWriter struct
    method Write (line 33) | func (w *printerWriter) Write(b []byte) (n int, err error) {
  function writerFromPrinter (line 29) | func writerFromPrinter(p Printer) io.Writer {

FILE: provide.go
  function Provide (line 64) | func Provide(constructors ...any) Option {
  type provideOption (line 71) | type provideOption struct
    method apply (line 76) | func (o provideOption) apply(mod *module) {
    method String (line 114) | func (o provideOption) String() string {
  type privateOption (line 97) | type privateOption struct
  function runProvide (line 122) | func runProvide(c container, p provide, opts ...dig.ProvideOption) error {

FILE: replace.go
  function Replace (line 77) | func Replace(values ...any) Option {
  type replaceOption (line 99) | type replaceOption struct
    method apply (line 105) | func (o replaceOption) apply(m *module) {
    method String (line 116) | func (o replaceOption) String() string {
  function newReplaceDecorator (line 125) | func newReplaceDecorator(value any) (any, reflect.Type) {

FILE: replace_test.go
  function TestReplaceSuccess (line 33) | func TestReplaceSuccess(t *testing.T) {
  function TestReplaceFailure (line 140) | func TestReplaceFailure(t *testing.T) {

FILE: shutdown.go
  type Shutdowner (line 31) | type Shutdowner interface
  type ShutdownOption (line 37) | type ShutdownOption interface
  type exitCodeOption (line 41) | type exitCodeOption
    method apply (line 43) | func (code exitCodeOption) apply(s *shutdowner) {
  function ExitCode (line 53) | func ExitCode(code int) ShutdownOption {
  type shutdownTimeoutOption (line 57) | type shutdownTimeoutOption
    method apply (line 59) | func (shutdownTimeoutOption) apply(*shutdowner) {}
  function ShutdownTimeout (line 69) | func ShutdownTimeout(timeout time.Duration) ShutdownOption {
  type shutdowner (line 73) | type shutdowner struct
    method Shutdown (line 81) | func (s *shutdowner) Shutdown(opts ...ShutdownOption) error {
  method shutdowner (line 92) | func (app *App) shutdowner() Shutdowner {

FILE: shutdown_test.go
  function TestShutdown (line 36) | func TestShutdown(t *testing.T) {
  function TestDataRace (line 174) | func TestDataRace(t *testing.T) {

FILE: signal.go
  type ShutdownSignal (line 37) | type ShutdownSignal struct
    method String (line 43) | func (sig ShutdownSignal) String() string {
  function newSignalReceivers (line 47) | func newSignalReceivers() signalReceivers {
  type signalReceivers (line 58) | type signalReceivers struct
    method relayer (line 79) | func (recv *signalReceivers) relayer() {
    method running (line 96) | func (recv *signalReceivers) running() bool {
    method Start (line 100) | func (recv *signalReceivers) Start() {
    method Stop (line 115) | func (recv *signalReceivers) Stop(ctx context.Context) error {
    method Done (line 140) | func (recv *signalReceivers) Done() <-chan os.Signal {
    method Wait (line 144) | func (recv *signalReceivers) Wait() <-chan ShutdownSignal {

FILE: signal_test.go
  function assertUnsentSignalError (line 34) | func assertUnsentSignalError(
  function TestSignal (line 49) | func TestSignal(t *testing.T) {

FILE: supply.go
  function Supply (line 82) | func Supply(values ...any) Option {
  type supplyOption (line 116) | type supplyOption struct
    method apply (line 123) | func (o supplyOption) apply(m *module) {
    method String (line 135) | func (o supplyOption) String() string {
  function newSupplyConstructor (line 144) | func newSupplyConstructor(value any) (any, reflect.Type) {

FILE: supply_test.go
  function TestSupply (line 38) | func TestSupply(t *testing.T) {

FILE: tools/analysis/passes/allfxevents/analysis.go
  function run (line 61) | func run(pass *analysis.Pass) (interface{}, error) {
  type visitor (line 80) | type visitor struct
    method Visit (line 91) | func (v *visitor) Visit(n ast.Node, push bool) (recurse bool) {
    method funcDeclEnter (line 132) | func (v *visitor) funcDeclEnter(n *ast.FuncDecl) bool {
    method funcDeclExit (line 152) | func (v *visitor) funcDeclExit(n *ast.FuncDecl) bool {
  function findPackage (line 184) | func findPackage(pkg *types.Package, importPath string) (_ *types.Packag...
  type fxevent (line 200) | type fxevent struct
  function inspectFxevent (line 208) | func inspectFxevent(pkg *types.Package) fxevent {
  type typeSet (line 245) | type typeSet struct
    method Len (line 247) | func (ts *typeSet) Len() int {
    method Put (line 252) | func (ts *typeSet) Put(t types.Type) {
    method Remove (line 258) | func (ts *typeSet) Remove(t types.Type) (found bool) {
    method Iterate (line 263) | func (ts *typeSet) Iterate(f func(types.Type)) {
    method Clone (line 269) | func (ts *typeSet) Clone() *typeSet {
  function emptyQualifier (line 277) | func emptyQualifier(*types.Package) string {

FILE: tools/analysis/passes/allfxevents/analysis_test.go
  function TestAnalyzer (line 29) | func TestAnalyzer(t *testing.T) {

FILE: tools/analysis/passes/allfxevents/testdata/src/a/full.go
  type fullLogger (line 9) | type fullLogger struct
    method LogEvent (line 11) | func (*fullLogger) LogEvent(ev fxevent.Event) {

FILE: tools/analysis/passes/allfxevents/testdata/src/a/nop.go
  type nopLogger (line 5) | type nopLogger struct
    method LogEvent (line 7) | func (nopLogger) LogEvent(fxevent.Event) {

FILE: tools/analysis/passes/allfxevents/testdata/src/a/not_a_logger.go
  type notALogger (line 9) | type notALogger struct
    method LogEvent (line 14) | func (*notALogger) LogEvent(ev fxevent.Event) error {

FILE: tools/analysis/passes/allfxevents/testdata/src/a/partial_test.go
  type partialLogger (line 11) | type partialLogger struct
    method LogEvent (line 13) | func (partialLogger) LogEvent(ev fxevent.Event) {

FILE: tools/analysis/passes/allfxevents/testdata/src/a/ptr.go
  type ptrLogger (line 9) | type ptrLogger struct
    method LogEvent (line 11) | func (*ptrLogger) LogEvent(ev fxevent.Event) { // want `\*ptrLogger do...

FILE: tools/analysis/passes/allfxevents/testdata/src/a/value.go
  type valueLogger (line 10) | type valueLogger struct
    method LogEvent (line 14) | func (l valueLogger) LogEvent(ev fxevent.Event) { // want `valueLogger...

FILE: tools/analysis/passes/allfxevents/testdata/src/b/fxevent/logger.go
  type Event (line 4) | type Event struct
  type Logger (line 5) | type Logger interface

FILE: tools/analysis/passes/allfxevents/testdata/src/b/not_real_fxevent.go
  type Logger (line 8) | type Logger struct
    method LogEvent (line 12) | func (Logger) LogEvent(ev fxevent.Event) {

FILE: tools/analysis/passes/allfxevents/testdata/src/go.uber.org/fx/fxevent/fxevent.go
  type Logger (line 7) | type Logger interface
  type Event (line 8) | type Event interface
  type Foo (line 9) | type Foo struct
    method event (line 15) | func (*Foo) event() {}
  type Bar (line 10) | type Bar struct
    method event (line 16) | func (*Bar) event() {}
  type Baz (line 11) | type Baz struct
    method event (line 17) | func (*Baz) event() {}
  type Qux (line 12) | type Qux struct
    method event (line 18) | func (*Qux) event() {}

FILE: tools/analysis/passes/allfxevents/testdata/src/go.uber.org/fx/fxevent/partial.go
  type partialLogger (line 4) | type partialLogger struct
    method LogEvent (line 6) | func (partialLogger) LogEvent(ev Event) { // want `partialLogger doesn...

FILE: tools/cmd/fxlint/main.go
  function main (line 33) | func main() {

FILE: version.go
  constant Version (line 24) | Version = "1.25.0-dev"
Condensed preview — 191 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (874K chars).
[
  {
    "path": ".codecov.yml",
    "chars": 821,
    "preview": "ignore:\n  - \"docs/ex/**/*.go\"\n  - \"internal/e2e/**/*.go\"\n\ncoverage:\n  range: 80..100\n  round: down\n  precision: 2\n\n  sta"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.md",
    "chars": 384,
    "preview": "---\nname: Bug report\nabout: Create a report to help us improve\ntitle: ''\nlabels: ''\nassignees: ''\n\n---\n\n**Describe the b"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/config.yml",
    "chars": 129,
    "preview": "contact_links:\n  - name: Questions\n    about: Please use our Discussions page\n    url: https://github.com/uber-go/fx/dis"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature_request.md",
    "chars": 769,
    "preview": "---\nname: Feature request\nabout: Suggest an idea for this project\ntitle: ''\nlabels: ''\nassignees: ''\n\n---\n\n**Is your fea"
  },
  {
    "path": ".github/dependabot.yml",
    "chars": 279,
    "preview": "version: 2\nupdates:\n  - package-ecosystem: \"github-actions\"\n    directory: \"/\"\n    schedule:\n      interval: \"weekly\"\n\n "
  },
  {
    "path": ".github/workflows/docs.yml",
    "chars": 1808,
    "preview": "name: GitHub Pages\n\non:\n  push:\n    tags: ['v*']\n\n  # This lets us publish the workflow\n  # manually from the GitHub Act"
  },
  {
    "path": ".github/workflows/fossa.yaml",
    "chars": 352,
    "preview": "name: FOSSA Analysis\non: push\n\njobs:\n\n  build:\n    runs-on: ubuntu-latest\n    if: github.repository_owner == 'uber-go'\n "
  },
  {
    "path": ".github/workflows/go.yml",
    "chars": 1442,
    "preview": "name: Go\n\non:\n  push:\n    branches: ['*']\n    tags: ['v*']\n  pull_request:\n    branches: ['*']\n\npermissions:\n  contents:"
  },
  {
    "path": ".gitignore",
    "chars": 128,
    "preview": "/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.md"
  },
  {
    "path": ".golangci.yml",
    "chars": 3088,
    "preview": "version: \"2\"\n\nissues:\n  # Print all issues reported by all linters.\n  max-issues-per-linter: 0\n  max-same-issues: 0\n\nlin"
  },
  {
    "path": "CHANGELOG.md",
    "chars": 19376,
    "preview": "---\nsearch:\n  exclude: true\n---\n\n# Changelog\n\nAll notable changes to this project will be documented in this file.\n\nThe "
  },
  {
    "path": "CONTRIBUTING.md",
    "chars": 7232,
    "preview": "---\nsearch:\n  exclude: true\n---\n\n# Contributing\n\nThanks for helping to make Fx better for everyone!\n\nIf you'd like to ad"
  },
  {
    "path": "LICENSE",
    "chars": 1072,
    "preview": "Copyright (c) 2016-2018 Uber Technologies, Inc.\n\nPermission is hereby granted, free of charge, to any person obtaining a"
  },
  {
    "path": "Makefile",
    "chars": 1435,
    "preview": "# Directory containing the Makefile.\nPROJECT_ROOT = $(dir $(abspath $(lastword $(MAKEFILE_LIST))))\n\nexport GOBIN ?= $(PR"
  },
  {
    "path": "README.md",
    "chars": 1753,
    "preview": "# :unicorn: Fx [![GoDoc](https://pkg.go.dev/badge/go.uber.org/fx)](https://pkg.go.dev/go.uber.org/fx) [![Github release]"
  },
  {
    "path": "annotated.go",
    "chars": 52630,
    "preview": "// Copyright (c) 2020-2021 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obt"
  },
  {
    "path": "annotated_test.go",
    "chars": 61005,
    "preview": "// Copyright (c) 2019-2021 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obt"
  },
  {
    "path": "app.go",
    "chars": 25884,
    "preview": "// Copyright (c) 2020-2021 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obt"
  },
  {
    "path": "app_internal_test.go",
    "chars": 4124,
    "preview": "// Copyright (c) 2019-2021 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obt"
  },
  {
    "path": "app_test.go",
    "chars": 68876,
    "preview": "// Copyright (c) 2023 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtainin"
  },
  {
    "path": "app_unixes.go",
    "chars": 1384,
    "preview": "// Copyright (c) 2021 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtainin"
  },
  {
    "path": "app_wasm.go",
    "chars": 1292,
    "preview": "// Copyright (c) 2023 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtainin"
  },
  {
    "path": "app_windows.go",
    "chars": 1272,
    "preview": "// Copyright (c) 2021 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtainin"
  },
  {
    "path": "app_windows_test.go",
    "chars": 3786,
    "preview": "// Copyright (c) 2020-2021 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obt"
  },
  {
    "path": "broadcast.go",
    "chars": 4597,
    "preview": "// Copyright (c) 2024 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtainin"
  },
  {
    "path": "decorate.go",
    "chars": 6765,
    "preview": "// Copyright (c) 2022 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtainin"
  },
  {
    "path": "decorate_test.go",
    "chars": 12432,
    "preview": "// Copyright (c) 2022 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtainin"
  },
  {
    "path": "doc.go",
    "chars": 11604,
    "preview": "// Copyright (c) 2019 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtainin"
  },
  {
    "path": "docs/.gitattributes",
    "chars": 100,
    "preview": "# Mark the uv.lock as generated so it is collapsed in review by default.\nuv.lock linguist-generated\n"
  },
  {
    "path": "docs/.gitignore",
    "chars": 7,
    "preview": "/_site\n"
  },
  {
    "path": "docs/Makefile",
    "chars": 85,
    "preview": ".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",
    "chars": 2219,
    "preview": "// Copyright (c) 2022 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtainin"
  },
  {
    "path": "docs/ex/annotate/cast_bad.go",
    "chars": 1629,
    "preview": "// Copyright (c) 2022 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtainin"
  },
  {
    "path": "docs/ex/annotate/cast_test.go",
    "chars": 1479,
    "preview": "// Copyright (c) 2022 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtainin"
  },
  {
    "path": "docs/ex/annotate/github/stub.go",
    "chars": 1199,
    "preview": "// Copyright (c) 2022 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtainin"
  },
  {
    "path": "docs/ex/annotate/sample.go",
    "chars": 1641,
    "preview": "// Copyright (c) 2022 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtainin"
  },
  {
    "path": "docs/ex/annotate/sample_test.go",
    "chars": 1804,
    "preview": "// Copyright (c) 2022 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtainin"
  },
  {
    "path": "docs/ex/get-started/01-minimal/main.go",
    "chars": 1239,
    "preview": "// Copyright (c) 2022 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtainin"
  },
  {
    "path": "docs/ex/get-started/01-minimal/main_test.go",
    "chars": 1255,
    "preview": "// Copyright (c) 2022 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtainin"
  },
  {
    "path": "docs/ex/get-started/02-http-server/main.go",
    "chars": 2170,
    "preview": "// Copyright (c) 2022 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtainin"
  },
  {
    "path": "docs/ex/get-started/02-http-server/main_test.go",
    "chars": 1502,
    "preview": "// Copyright (c) 2022 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtainin"
  },
  {
    "path": "docs/ex/get-started/03-echo-handler/main.go",
    "chars": 2979,
    "preview": "// Copyright (c) 2022 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtainin"
  },
  {
    "path": "docs/ex/get-started/03-echo-handler/main_test.go",
    "chars": 1453,
    "preview": "// Copyright (c) 2022 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtainin"
  },
  {
    "path": "docs/ex/get-started/04-logger/main.go",
    "chars": 3169,
    "preview": "// Copyright (c) 2022 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtainin"
  },
  {
    "path": "docs/ex/get-started/04-logger/main_test.go",
    "chars": 1453,
    "preview": "// Copyright (c) 2022 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtainin"
  },
  {
    "path": "docs/ex/get-started/05-registration/main.go",
    "chars": 3422,
    "preview": "// Copyright (c) 2022 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtainin"
  },
  {
    "path": "docs/ex/get-started/05-registration/main_test.go",
    "chars": 1453,
    "preview": "// Copyright (c) 2022 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtainin"
  },
  {
    "path": "docs/ex/get-started/06-another-handler/main.go",
    "chars": 5048,
    "preview": "// Copyright (c) 2022 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtainin"
  },
  {
    "path": "docs/ex/get-started/06-another-handler/main_test.go",
    "chars": 1651,
    "preview": "// Copyright (c) 2022 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtainin"
  },
  {
    "path": "docs/ex/get-started/07-many-handlers/main.go",
    "chars": 4765,
    "preview": "// Copyright (c) 2022 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtainin"
  },
  {
    "path": "docs/ex/get-started/07-many-handlers/main_test.go",
    "chars": 1651,
    "preview": "// Copyright (c) 2022 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtainin"
  },
  {
    "path": "docs/ex/modules/module.go",
    "chars": 2679,
    "preview": "// Copyright (c) 2022 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtainin"
  },
  {
    "path": "docs/ex/modules/module_test.go",
    "chars": 1454,
    "preview": "// Copyright (c) 2022 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtainin"
  },
  {
    "path": "docs/ex/parameter-objects/define.go",
    "chars": 2039,
    "preview": "// Copyright (c) 2022 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtainin"
  },
  {
    "path": "docs/ex/parameter-objects/define_test.go",
    "chars": 1688,
    "preview": "// Copyright (c) 2022 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtainin"
  },
  {
    "path": "docs/ex/parameter-objects/extend.go",
    "chars": 1781,
    "preview": "// Copyright (c) 2022 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtainin"
  },
  {
    "path": "docs/ex/parameter-objects/extend_test.go",
    "chars": 1927,
    "preview": "// Copyright (c) 2022 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtainin"
  },
  {
    "path": "docs/ex/result-objects/define.go",
    "chars": 1794,
    "preview": "// Copyright (c) 2022 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtainin"
  },
  {
    "path": "docs/ex/result-objects/define_test.go",
    "chars": 1435,
    "preview": "// Copyright (c) 2022 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtainin"
  },
  {
    "path": "docs/ex/result-objects/extend.go",
    "chars": 1814,
    "preview": "// Copyright (c) 2022 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtainin"
  },
  {
    "path": "docs/ex/result-objects/extend_test.go",
    "chars": 1512,
    "preview": "// Copyright (c) 2022 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtainin"
  },
  {
    "path": "docs/ex/value-groups/consume/annotate.go",
    "chars": 2561,
    "preview": "// Copyright (c) 2022 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtainin"
  },
  {
    "path": "docs/ex/value-groups/consume/consume_test.go",
    "chars": 1874,
    "preview": "// Copyright (c) 2022 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtainin"
  },
  {
    "path": "docs/ex/value-groups/consume/param.go",
    "chars": 2070,
    "preview": "// Copyright (c) 2022 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtainin"
  },
  {
    "path": "docs/ex/value-groups/feed/annotate.go",
    "chars": 2362,
    "preview": "// Copyright (c) 2022 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtainin"
  },
  {
    "path": "docs/ex/value-groups/feed/feed_test.go",
    "chars": 1737,
    "preview": "// Copyright (c) 2022 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtainin"
  },
  {
    "path": "docs/ex/value-groups/feed/result.go",
    "chars": 2048,
    "preview": "// Copyright (c) 2022 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtainin"
  },
  {
    "path": "docs/go.mod",
    "chars": 456,
    "preview": "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/"
  },
  {
    "path": "docs/go.sum",
    "chars": 2343,
    "preview": "github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/davecgh/go-spew v1.1"
  },
  {
    "path": "docs/internal/apptest/run.go",
    "chars": 4105,
    "preview": "// Copyright (c) 2022 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtainin"
  },
  {
    "path": "docs/internal/apptest/run_test.go",
    "chars": 2859,
    "preview": "// Copyright (c) 2022 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtainin"
  },
  {
    "path": "docs/internal/exectest/cmd.go",
    "chars": 2378,
    "preview": "// Copyright (c) 2022 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtainin"
  },
  {
    "path": "docs/internal/exectest/cmd_test.go",
    "chars": 1994,
    "preview": "// Copyright (c) 2022 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtainin"
  },
  {
    "path": "docs/internal/exectest/output.go",
    "chars": 2035,
    "preview": "// Copyright (c) 2022 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtainin"
  },
  {
    "path": "docs/internal/exectest/output_test.go",
    "chars": 2143,
    "preview": "// Copyright (c) 2022 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtainin"
  },
  {
    "path": "docs/internal/httptest/http.go",
    "chars": 1885,
    "preview": "// Copyright (c) 2022 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtainin"
  },
  {
    "path": "docs/internal/httptest/http_test.go",
    "chars": 2078,
    "preview": "// Copyright (c) 2022 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtainin"
  },
  {
    "path": "docs/internal/iotest/read.go",
    "chars": 1488,
    "preview": "// Copyright (c) 2022 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtainin"
  },
  {
    "path": "docs/internal/iotest/read_test.go",
    "chars": 1782,
    "preview": "// Copyright (c) 2022 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtainin"
  },
  {
    "path": "docs/internal/test/fake.go",
    "chars": 2432,
    "preview": "// Copyright (c) 2022 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtainin"
  },
  {
    "path": "docs/internal/test/fake_test.go",
    "chars": 1792,
    "preview": "// Copyright (c) 2022 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtainin"
  },
  {
    "path": "docs/internal/test/t.go",
    "chars": 1325,
    "preview": "// Copyright (c) 2022 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtainin"
  },
  {
    "path": "docs/internal/test/t_test.go",
    "chars": 1182,
    "preview": "// Copyright (c) 2022 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtainin"
  },
  {
    "path": "docs/mkdocs.yml",
    "chars": 5334,
    "preview": "site_name: Fx\nsite_url: https://uber-go.github.io/fx\nsite_description: >-\n  A dependency injection system for Go.\n\nrepo_"
  },
  {
    "path": "docs/pyproject.toml",
    "chars": 205,
    "preview": "[tool.uv]\ndev-dependencies = [\n    \"mkdocs-material>=9.5.33\",\n    \"mkdocs-git-revision-date-localized-plugin>=1.2.7\",\n  "
  },
  {
    "path": "docs/src/annotate.md",
    "chars": 2698,
    "preview": "# Annotations\n\nYou can annotate functions and values with the `fx.Annotate` function\nbefore passing them to\n`fx.Provide`"
  },
  {
    "path": "docs/src/container.md",
    "chars": 4520,
    "preview": "# Container\n\nContainer is the abstraction responsible for holding all constructors and values.\nIt’s the primary means by"
  },
  {
    "path": "docs/src/faq.md",
    "chars": 2314,
    "preview": "# Frequently Asked Questions\n\nThis page contains answers to common questions and issues with using Fx.\n\n## Does the orde"
  },
  {
    "path": "docs/src/get-started/another-handler.md",
    "chars": 4619,
    "preview": "# Register another handler\n\nThe handler we defined above has a single handler.\nLet's add another.\n\n1. Build a new handle"
  },
  {
    "path": "docs/src/get-started/conclusion.md",
    "chars": 331,
    "preview": "# Conclusion\n\nThis marks the end of this tutorial.\nIn this tutorial, we covered,\n\n- [x] how to start an Fx application f"
  },
  {
    "path": "docs/src/get-started/echo-handler.md",
    "chars": 2521,
    "preview": "# 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"
  },
  {
    "path": "docs/src/get-started/http-server.md",
    "chars": 3498,
    "preview": "# Add an HTTP server\n\nIn the previous section, we wrote a minimal Fx application\nthat doesn't do anything.\nLet's add an "
  },
  {
    "path": "docs/src/get-started/index.md",
    "chars": 771,
    "preview": "# Get started with Fx\n\n\nThis introduces you to the basics of Fx.\nIn this tutorial you will:\n\n- [start an empty applicati"
  },
  {
    "path": "docs/src/get-started/logger.md",
    "chars": 3437,
    "preview": "# Add a logger\n\nOur application currently prints\nthe \"Starting HTTP server\" message to standard out,\nand errors to stand"
  },
  {
    "path": "docs/src/get-started/many-handlers.md",
    "chars": 3561,
    "preview": "# Register many handlers\n\nWe added two handlers in the previous section,\nbut we reference them both explicitly by name w"
  },
  {
    "path": "docs/src/get-started/minimal.md",
    "chars": 1331,
    "preview": "# Create a minimal application\n\nLet's build the hello-world equivalent of an Fx application.\nThis application won't do a"
  },
  {
    "path": "docs/src/get-started/registration.md",
    "chars": 2841,
    "preview": "# Decouple registration\n\n`NewServeMux` above declares an explicit dependency on `EchoHandler`.\nThis is an unnecessarily "
  },
  {
    "path": "docs/src/index.md",
    "chars": 1302,
    "preview": "# Fx\n\nFx is **a dependency injection system for Go**.\n\n<div class=\"grid cards\" markdown>\n\n- **Eliminate globals**\n\n    -"
  },
  {
    "path": "docs/src/lifecycle.md",
    "chars": 1806,
    "preview": "# Application lifecycle\n\nThe lifecycle of an Fx application has two high-level phases:\n*initialization* and *execution*."
  },
  {
    "path": "docs/src/modules.md",
    "chars": 7261,
    "preview": "---\nsidebarDepth: 2\n---\n\n# Modules\n\nAn Fx module is a shareable Go library or package\nthat provides self-contained funct"
  },
  {
    "path": "docs/src/parameter-objects.md",
    "chars": 2688,
    "preview": "# Parameter Objects\n\nA parameter object is an object with the sole purpose of carrying parameters\nfor a specific functio"
  },
  {
    "path": "docs/src/result-objects.md",
    "chars": 2366,
    "preview": "# Result Objects\n\nA result object is an object with the sole purpose of carrying results\nfor a specific function or meth"
  },
  {
    "path": "docs/src/value-groups/consume.md",
    "chars": 2636,
    "preview": "# Consuming value groups\n\nTo consume a value group of type `T`,\nyou have to tag a `[]T` dependency with `group:\"$name\"`\n"
  },
  {
    "path": "docs/src/value-groups/feed.md",
    "chars": 2504,
    "preview": "# Feeding value groups\n\nTo feed values to a value group of type `T`,\nyou have to tag a `T` result with `group:\"$name\"`\nw"
  },
  {
    "path": "docs/src/value-groups/index.md",
    "chars": 2583,
    "preview": "# Value Groups\n\nA *value group* is a collection of values of the same type.\nAny number of constructors across an Fx appl"
  },
  {
    "path": "error_example_test.go",
    "chars": 1795,
    "preview": "// Copyright (c) 2019 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtainin"
  },
  {
    "path": "example_test.go",
    "chars": 9713,
    "preview": "// Copyright (c) 2019 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtainin"
  },
  {
    "path": "extract.go",
    "chars": 4117,
    "preview": "// Copyright (c) 2019 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtainin"
  },
  {
    "path": "extract_test.go",
    "chars": 8101,
    "preview": "// Copyright (c) 2019 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtainin"
  },
  {
    "path": "fxevent/console.go",
    "chars": 5154,
    "preview": "// Copyright (c) 2021 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtainin"
  },
  {
    "path": "fxevent/console_test.go",
    "chars": 12465,
    "preview": "// Copyright (c) 2021 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtainin"
  },
  {
    "path": "fxevent/doc.go",
    "chars": 2808,
    "preview": "// Copyright (c) 2024 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtainin"
  },
  {
    "path": "fxevent/event.go",
    "chars": 9388,
    "preview": "// Copyright (c) 2021 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtainin"
  },
  {
    "path": "fxevent/event_test.go",
    "chars": 1718,
    "preview": "// Copyright (c) 2021 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtainin"
  },
  {
    "path": "fxevent/logger.go",
    "chars": 1522,
    "preview": "// Copyright (c) 2021 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtainin"
  },
  {
    "path": "fxevent/slog.go",
    "chars": 8065,
    "preview": "// Copyright (c) 2021 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtainin"
  },
  {
    "path": "fxevent/slog_test.go",
    "chars": 15411,
    "preview": "// Copyright (c) 2021 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtainin"
  },
  {
    "path": "fxevent/zap.go",
    "chars": 7106,
    "preview": "// Copyright (c) 2021 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtainin"
  },
  {
    "path": "fxevent/zap_test.go",
    "chars": 13697,
    "preview": "// Copyright (c) 2021 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtainin"
  },
  {
    "path": "fxtest/app.go",
    "chars": 2427,
    "preview": "// Copyright (c) 2019-2021 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obt"
  },
  {
    "path": "fxtest/app_test.go",
    "chars": 2850,
    "preview": "// Copyright (c) 2019-2021 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obt"
  },
  {
    "path": "fxtest/doc.go",
    "chars": 1233,
    "preview": "// Copyright (c) 2024 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtainin"
  },
  {
    "path": "fxtest/lifecycle.go",
    "chars": 5232,
    "preview": "// Copyright (c) 2020-2021 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obt"
  },
  {
    "path": "fxtest/lifecycle_test.go",
    "chars": 7736,
    "preview": "// Copyright (c) 2020 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtainin"
  },
  {
    "path": "fxtest/printer.go",
    "chars": 1917,
    "preview": "// Copyright (c) 2019-2021 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obt"
  },
  {
    "path": "fxtest/printer_test.go",
    "chars": 1403,
    "preview": "// Copyright (c) 2019 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtainin"
  },
  {
    "path": "fxtest/tb.go",
    "chars": 1336,
    "preview": "// Copyright (c) 2020 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtainin"
  },
  {
    "path": "fxtest/tb_test.go",
    "chars": 1677,
    "preview": "// Copyright (c) 2020 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtainin"
  },
  {
    "path": "go.mod",
    "chars": 387,
    "preview": "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/gole"
  },
  {
    "path": "go.sum",
    "chars": 3129,
    "preview": "github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/davecgh/go-spew v1.1"
  },
  {
    "path": "inout.go",
    "chars": 2020,
    "preview": "// Copyright (c) 2019 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtainin"
  },
  {
    "path": "inout_test.go",
    "chars": 4360,
    "preview": "// Copyright (c) 2019 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtainin"
  },
  {
    "path": "internal/e2e/README.md",
    "chars": 267,
    "preview": "This directory holds end-to-end tests for Fx.\nEach subdirectory holds a complete Fx application\nand a test for it.\n\nThis"
  },
  {
    "path": "internal/e2e/go.mod",
    "chars": 478,
    "preview": "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\nre"
  },
  {
    "path": "internal/e2e/go.sum",
    "chars": 2343,
    "preview": "github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/davecgh/go-spew v1.1"
  },
  {
    "path": "internal/e2e/shutdowner_run_exitcode/main.go",
    "chars": 1311,
    "preview": "// Copyright (c) 2023 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtainin"
  },
  {
    "path": "internal/e2e/shutdowner_run_exitcode/main_test.go",
    "chars": 2333,
    "preview": "// Copyright (c) 2023 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtainin"
  },
  {
    "path": "internal/e2e/shutdowner_wait_exitcode/main.go",
    "chars": 1603,
    "preview": "// Copyright (c) 2023 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtainin"
  },
  {
    "path": "internal/e2e/shutdowner_wait_exitcode/main_test.go",
    "chars": 2333,
    "preview": "// Copyright (c) 2023 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtainin"
  },
  {
    "path": "internal/fxclock/clock.go",
    "chars": 7344,
    "preview": "// Copyright (c) 2024 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtainin"
  },
  {
    "path": "internal/fxclock/clock_test.go",
    "chars": 5215,
    "preview": "// Copyright (c) 2024 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtainin"
  },
  {
    "path": "internal/fxlog/default.go",
    "chars": 1325,
    "preview": "// Copyright (c) 2021 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtainin"
  },
  {
    "path": "internal/fxlog/default_test.go",
    "chars": 1445,
    "preview": "// Copyright (c) 2020-2021 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obt"
  },
  {
    "path": "internal/fxlog/foovendor/foovendor.go",
    "chars": 1231,
    "preview": "// Copyright (c) 2019 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtainin"
  },
  {
    "path": "internal/fxlog/sample.git/sample.go",
    "chars": 1229,
    "preview": "// Copyright (c) 2019 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtainin"
  },
  {
    "path": "internal/fxlog/spy.go",
    "chars": 2590,
    "preview": "// Copyright (c) 2020-2021 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obt"
  },
  {
    "path": "internal/fxlog/spy_test.go",
    "chars": 2177,
    "preview": "// Copyright (c) 2020 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtainin"
  },
  {
    "path": "internal/fxreflect/fxreflect.go",
    "chars": 2961,
    "preview": "// Copyright (c) 2019-2021 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obt"
  },
  {
    "path": "internal/fxreflect/fxreflect_test.go",
    "chars": 2527,
    "preview": "// Copyright (c) 2019-2021 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obt"
  },
  {
    "path": "internal/fxreflect/stack.go",
    "chars": 4377,
    "preview": "// Copyright (c) 2019 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtainin"
  },
  {
    "path": "internal/fxreflect/stack_test.go",
    "chars": 6247,
    "preview": "// Copyright (c) 2019 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtainin"
  },
  {
    "path": "internal/leaky_test/leaky_test.go",
    "chars": 2530,
    "preview": "// Copyright (c) 2022 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtainin"
  },
  {
    "path": "internal/lifecycle/lifecycle.go",
    "chars": 10580,
    "preview": "// Copyright (c) 2022 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtainin"
  },
  {
    "path": "internal/lifecycle/lifecycle_test.go",
    "chars": 10040,
    "preview": "// Copyright (c) 2019 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtainin"
  },
  {
    "path": "internal/testutil/writer.go",
    "chars": 1680,
    "preview": "// Copyright (c) 2020-2021 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obt"
  },
  {
    "path": "internal/testutil/writer_test.go",
    "chars": 1770,
    "preview": "// Copyright (c) 2020 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtainin"
  },
  {
    "path": "invoke.go",
    "chars": 3575,
    "preview": "// Copyright (c) 2019-2021 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obt"
  },
  {
    "path": "lifecycle.go",
    "chars": 4549,
    "preview": "// Copyright (c) 2022 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtainin"
  },
  {
    "path": "log.go",
    "chars": 1732,
    "preview": "// Copyright (c) 2021 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtainin"
  },
  {
    "path": "log_test.go",
    "chars": 2228,
    "preview": "// Copyright (c) 2021 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtainin"
  },
  {
    "path": "module.go",
    "chars": 11631,
    "preview": "// Copyright (c) 2022 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtainin"
  },
  {
    "path": "module_test.go",
    "chars": 17321,
    "preview": "// Copyright (c) 2022 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtainin"
  },
  {
    "path": "populate.go",
    "chars": 3504,
    "preview": "// Copyright (c) 2019 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtainin"
  },
  {
    "path": "populate_example_test.go",
    "chars": 1886,
    "preview": "// Copyright (c) 2019 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtainin"
  },
  {
    "path": "populate_test.go",
    "chars": 8623,
    "preview": "// Copyright (c) 2019 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtainin"
  },
  {
    "path": "printer_writer.go",
    "chars": 1505,
    "preview": "// Copyright (c) 2020-2021 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obt"
  },
  {
    "path": "provide.go",
    "chars": 6426,
    "preview": "// Copyright (c) 2022 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtainin"
  },
  {
    "path": "replace.go",
    "chars": 4303,
    "preview": "// Copyright (c) 2022 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtainin"
  },
  {
    "path": "replace_test.go",
    "chars": 4914,
    "preview": "// Copyright (c) 2022 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtainin"
  },
  {
    "path": "shutdown.go",
    "chars": 3237,
    "preview": "// Copyright (c) 2019 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtainin"
  },
  {
    "path": "shutdown_test.go",
    "chars": 5657,
    "preview": "// Copyright (c) 2019 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtainin"
  },
  {
    "path": "signal.go",
    "chars": 4202,
    "preview": "// Copyright (c) 2022 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtainin"
  },
  {
    "path": "signal_test.go",
    "chars": 4141,
    "preview": "// Copyright (c) 2022 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtainin"
  },
  {
    "path": "supply.go",
    "chars": 4697,
    "preview": "// Copyright (c) 2020 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtainin"
  },
  {
    "path": "supply_test.go",
    "chars": 4842,
    "preview": "// Copyright (c) 2020 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtainin"
  },
  {
    "path": "tools/analysis/passes/allfxevents/analysis.go",
    "chars": 6995,
    "preview": "// Copyright (c) 2021 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtainin"
  },
  {
    "path": "tools/analysis/passes/allfxevents/analysis_test.go",
    "chars": 1330,
    "preview": "// Copyright (c) 2021 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtainin"
  },
  {
    "path": "tools/analysis/passes/allfxevents/testdata/src/a/full.go",
    "chars": 260,
    "preview": "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 fxeven"
  },
  {
    "path": "tools/analysis/passes/allfxevents/testdata/src/a/nop.go",
    "chars": 187,
    "preview": "package a\n\nimport \"go.uber.org/fx/fxevent\"\n\ntype nopLogger struct{}\n\nfunc (nopLogger) LogEvent(fxevent.Event) {\n\t// Don'"
  },
  {
    "path": "tools/analysis/passes/allfxevents/testdata/src/a/not_a_logger.go",
    "chars": 300,
    "preview": "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 b"
  },
  {
    "path": "tools/analysis/passes/allfxevents/testdata/src/a/partial_test.go",
    "chars": 319,
    "preview": "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'"
  },
  {
    "path": "tools/analysis/passes/allfxevents/testdata/src/a/ptr.go",
    "chars": 295,
    "preview": "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."
  },
  {
    "path": "tools/analysis/passes/allfxevents/testdata/src/a/value.go",
    "chars": 321,
    "preview": "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 valueLo"
  },
  {
    "path": "tools/analysis/passes/allfxevents/testdata/src/b/fxevent/logger.go",
    "chars": 80,
    "preview": "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",
    "chars": 160,
    "preview": "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"
  },
  {
    "path": "tools/analysis/passes/allfxevents/testdata/src/go.uber.org/fx/fxevent/fxevent.go",
    "chars": 385,
    "preview": "package fxevent\n\n// This is a partial fxevent package inspired by the real fxevent package,\n// but with a fixed list of "
  },
  {
    "path": "tools/analysis/passes/allfxevents/testdata/src/go.uber.org/fx/fxevent/partial.go",
    "chars": 270,
    "preview": "package fxevent\n\n// Partial logger implementation in the same package as fxevent.Logger.\ntype partialLogger struct{}\n\nfu"
  },
  {
    "path": "tools/cmd/fxlint/main.go",
    "chars": 1502,
    "preview": "// Copyright (c) 2021 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtainin"
  },
  {
    "path": "tools/go.mod",
    "chars": 185,
    "preview": "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/"
  },
  {
    "path": "tools/go.sum",
    "chars": 632,
    "preview": "github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=\ngithub.com/google/go-cmp v0.6.0/go.mod h"
  },
  {
    "path": "version.go",
    "chars": 1220,
    "preview": "// Copyright (c) 2019 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtainin"
  }
]

About this extraction

This page contains the full source code of the uber-go/fx GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 191 files (770.9 KB), approximately 215.8k tokens, and a symbol index with 771 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!