imbue

package module
v0.7.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 13, 2023 License: MIT Imports: 11 Imported by: 6

README

Imbue

Imbue is a type-safe dependency injection container for Go.

Documentation Latest Version Build Status Code Coverage

It supports named dependencies, dependency groups, and deferring closure of dependencies until the container itself is closed.

Documentation

Overview

Package imbue is a type-safe dependency injection container.

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func Decorate0 added in v0.4.0

func Decorate0[T any](
	con ContainerAware,
	dec func(Context, T) (T, error),
	options ...DecorateOption,
)

Decorate0 describes how to decorate values of type T after construction.

The dependency being decorated is passed to dec and replaced with the decorator's return value.

The decorated dependency may be manipulated in-place.

Example
package main

import (
	"fmt"

	"github.com/dogmatiq/imbue"
)

func main() {
	con := imbue.New()
	defer con.Close()

	// Declare some types to use as dependencies within the example.
	type DecoratedDependency struct {
		Value string
	}

	// Declare a decorator for the DecoratedDependency type.
	imbue.Decorate0(
		con,
		func(
			ctx imbue.Context,
			d *DecoratedDependency,
		) (*DecoratedDependency, error) {
			d.Value = "<decorated value>"
			return d, nil
		},
	)

	// Print the dependency tree.
	fmt.Println(con)
}
Output:

<container>
└── *imbue_test.DecoratedDependency
Example (HttpServeMux)
package main

import (
	"fmt"
	"net/http"

	"github.com/dogmatiq/imbue"
)

func main() {
	con := imbue.New()
	defer con.Close()

	// This example illustrates how decoration can be used to add routes to an
	// http.ServeMux that is declared in a separate location.

	imbue.With0(
		con,
		func(
			ctx imbue.Context,
		) (*http.ServeMux, error) {
			return http.NewServeMux(), nil
		},
	)

	imbue.Decorate0(
		con,
		func(
			ctx imbue.Context,
			mux *http.ServeMux,
		) (*http.ServeMux, error) {
			mux.HandleFunc("/account", func(w http.ResponseWriter, r *http.Request) {
				fmt.Fprintf(w, "account handler")
			})

			mux.HandleFunc("/dashboard", func(w http.ResponseWriter, r *http.Request) {
				fmt.Fprintf(w, "dashboard handler")
			})

			return mux, nil
		},
	)

	// Print the dependency tree.
	fmt.Println(con)
}
Output:

<container>
└── *http.ServeMux

func Decorate1 added in v0.4.0

func Decorate1[T, D any](
	con ContainerAware,
	dec func(Context, T, D) (T, error),
	options ...DecorateOption,
)

Decorate1 describes how to decorate values of type T after construction using a single additional dependency.

The dependency being decorated is passed to dec and replaced with the decorator's return value.

The decorated dependency may be manipulated in-place.

Example
package main

import (
	"fmt"

	"github.com/dogmatiq/imbue"
)

func main() {
	con := imbue.New()
	defer con.Close()

	// Declare some types to use as dependencies within the example.
	type UpstreamDependency struct{}
	type DecoratedDependency struct {
		Up *UpstreamDependency
	}

	// Declare a decorator for the DecoratedDependency type. It depends on the
	// upstream dependency type (which is assumed to be declared elsewhere).
	imbue.Decorate1(
		con,
		func(
			ctx imbue.Context,
			d *DecoratedDependency,
			up *UpstreamDependency,
		) (*DecoratedDependency, error) {
			d.Up = up
			return d, nil
		},
	)

	// Print the dependency tree.
	fmt.Println(con)
}
Output:

<container>
└── *imbue_test.DecoratedDependency
    └── *imbue_test.UpstreamDependency

func Decorate2 added in v0.4.0

func Decorate2[T, D1, D2 any](
	con ContainerAware,
	dec func(Context, T, D1, D2) (T, error),
	options ...DecorateOption,
)

Decorate2 describes how to decorate values of type T after construction using 2 additional dependencies.

The dependency being decorated is passed to dec and replaced with the decorator's return value.

The decorated dependency may be manipulated in-place.

Example
package main

import (
	"fmt"

	"github.com/dogmatiq/imbue"
)

func main() {
	con := imbue.New()
	defer con.Close()

	// Declare some types to use as dependencies within the example.
	type UpstreamDependency1 struct{}
	type UpstreamDependency2 struct{}
	type DecoratedDependency struct {
		Up1 *UpstreamDependency1
		Up2 *UpstreamDependency2
	}

	// Declare a decorator for the DecoratedDependency type. It depends on the
	// two upstream dependency types (which are assumed to be declared
	// elsewhere).
	imbue.Decorate2(
		con,
		func(
			ctx imbue.Context,
			d *DecoratedDependency,
			up1 *UpstreamDependency1,
			up2 *UpstreamDependency2,
		) (*DecoratedDependency, error) {
			d.Up1 = up1
			d.Up2 = up2
			return d, nil
		},
	)

	// Print the dependency tree.
	fmt.Println(con)
}
Output:

<container>
└── *imbue_test.DecoratedDependency
    ├── *imbue_test.UpstreamDependency1
    └── *imbue_test.UpstreamDependency2

func Decorate3 added in v0.4.0

func Decorate3[T, D1, D2, D3 any](
	con ContainerAware,
	dec func(Context, T, D1, D2, D3) (T, error),
	options ...DecorateOption,
)

Decorate3 describes how to decorate values of type T after construction using 3 additional dependencies.

The dependency being decorated is passed to dec and replaced with the decorator's return value.

The decorated dependency may be manipulated in-place.

func Decorate4 added in v0.4.0

func Decorate4[T, D1, D2, D3, D4 any](
	con ContainerAware,
	dec func(Context, T, D1, D2, D3, D4) (T, error),
	options ...DecorateOption,
)

Decorate4 describes how to decorate values of type T after construction using 4 additional dependencies.

The dependency being decorated is passed to dec and replaced with the decorator's return value.

The decorated dependency may be manipulated in-place.

func Decorate5 added in v0.4.0

func Decorate5[T, D1, D2, D3, D4, D5 any](
	con ContainerAware,
	dec func(Context, T, D1, D2, D3, D4, D5) (T, error),
	options ...DecorateOption,
)

Decorate5 describes how to decorate values of type T after construction using 5 additional dependencies.

The dependency being decorated is passed to dec and replaced with the decorator's return value.

The decorated dependency may be manipulated in-place.

func Decorate6 added in v0.4.0

func Decorate6[T, D1, D2, D3, D4, D5, D6 any](
	con ContainerAware,
	dec func(Context, T, D1, D2, D3, D4, D5, D6) (T, error),
	options ...DecorateOption,
)

Decorate6 describes how to decorate values of type T after construction using 6 additional dependencies.

The dependency being decorated is passed to dec and replaced with the decorator's return value.

The decorated dependency may be manipulated in-place.

func Decorate7 added in v0.4.0

func Decorate7[T, D1, D2, D3, D4, D5, D6, D7 any](
	con ContainerAware,
	dec func(Context, T, D1, D2, D3, D4, D5, D6, D7) (T, error),
	options ...DecorateOption,
)

Decorate7 describes how to decorate values of type T after construction using 7 additional dependencies.

The dependency being decorated is passed to dec and replaced with the decorator's return value.

The decorated dependency may be manipulated in-place.

func Decorate8 added in v0.4.0

func Decorate8[T, D1, D2, D3, D4, D5, D6, D7, D8 any](
	con ContainerAware,
	dec func(Context, T, D1, D2, D3, D4, D5, D6, D7, D8) (T, error),
	options ...DecorateOption,
)

Decorate8 describes how to decorate values of type T after construction using 8 additional dependencies.

The dependency being decorated is passed to dec and replaced with the decorator's return value.

The decorated dependency may be manipulated in-place.

func Go1 added in v0.6.2

func Go1[D any](
	g *WaitGroup,
	fn func(context.Context, D) error,
	options ...InvokeOption,
)

Go1 starts a new goroutine by calling a function with a single dependency.

func Go2 added in v0.6.2

func Go2[D1, D2 any](
	g *WaitGroup,
	fn func(context.Context, D1, D2) error,
	options ...InvokeOption,
)

Go2 starts a new goroutine by calling a function with 2 dependencies.

func Go3 added in v0.6.2

func Go3[D1, D2, D3 any](
	g *WaitGroup,
	fn func(context.Context, D1, D2, D3) error,
	options ...InvokeOption,
)

Go3 starts a new goroutine by calling a function with 3 dependencies.

func Go4 added in v0.6.2

func Go4[D1, D2, D3, D4 any](
	g *WaitGroup,
	fn func(context.Context, D1, D2, D3, D4) error,
	options ...InvokeOption,
)

Go4 starts a new goroutine by calling a function with 4 dependencies.

func Go5 added in v0.6.2

func Go5[D1, D2, D3, D4, D5 any](
	g *WaitGroup,
	fn func(context.Context, D1, D2, D3, D4, D5) error,
	options ...InvokeOption,
)

Go5 starts a new goroutine by calling a function with 5 dependencies.

func Go6 added in v0.6.2

func Go6[D1, D2, D3, D4, D5, D6 any](
	g *WaitGroup,
	fn func(context.Context, D1, D2, D3, D4, D5, D6) error,
	options ...InvokeOption,
)

Go6 starts a new goroutine by calling a function with 6 dependencies.

func Go7 added in v0.6.2

func Go7[D1, D2, D3, D4, D5, D6, D7 any](
	g *WaitGroup,
	fn func(context.Context, D1, D2, D3, D4, D5, D6, D7) error,
	options ...InvokeOption,
)

Go7 starts a new goroutine by calling a function with 7 dependencies.

func Go8 added in v0.6.2

func Go8[D1, D2, D3, D4, D5, D6, D7, D8 any](
	g *WaitGroup,
	fn func(context.Context, D1, D2, D3, D4, D5, D6, D7, D8) error,
	options ...InvokeOption,
)

Go8 starts a new goroutine by calling a function with 8 dependencies.

func Invoke0 added in v0.6.1

func Invoke0(
	ctx context.Context,
	con *Container,
	fn func(context.Context) error,
	options ...InvokeOption,
) error

Invoke0 calls a function without dependencies.

This function does not use the container at all; it is included to aid while refactoring.

func Invoke1

func Invoke1[D any](
	ctx context.Context,
	con *Container,
	fn func(context.Context, D) error,
	options ...InvokeOption,
) error

Invoke1 calls a function with a single dependency.

Example
package main

import (
	"context"
	"fmt"

	"github.com/dogmatiq/imbue"
)

func main() {
	con := imbue.New()
	defer con.Close()

	// Declare some types to use as dependencies within the example.
	type Dependency struct{ Value string }

	// Declare a constructor for the Dependency type.
	imbue.With0(
		con,
		func(ctx imbue.Context) (Dependency, error) {
			return Dependency{"<value>"}, nil
		},
	)

	// Invoke a function that depends on the two dependency types declared
	// above.
	if err := imbue.Invoke1(
		context.Background(),
		con,
		func(
			ctx context.Context,
			dep Dependency,
		) error {
			fmt.Println(dep)
			return nil
		},
	); err != nil {
		panic(err)
	}
}
Output:

{<value>}

func Invoke2

func Invoke2[D1, D2 any](
	ctx context.Context,
	con *Container,
	fn func(context.Context, D1, D2) error,
	options ...InvokeOption,
) error

Invoke2 calls a function with 2 dependencies.

Example
package main

import (
	"context"
	"fmt"

	"github.com/dogmatiq/imbue"
)

func main() {
	con := imbue.New()
	defer con.Close()

	// Declare some types to use as dependencies within the example.
	type Dependency1 struct{ Value string }
	type Dependency2 struct{ Value string }

	// Declare a constructor for the Dependency1 type.
	imbue.With0(
		con,
		func(ctx imbue.Context) (Dependency1, error) {
			return Dependency1{"<value-1>"}, nil
		},
	)

	// Declare a constructor for the Dependency2 type.
	imbue.With0(
		con,
		func(ctx imbue.Context) (Dependency2, error) {
			return Dependency2{"<value-2>"}, nil
		},
	)

	// Invoke a function that depends on the two dependency types declared
	// above.
	if err := imbue.Invoke2(
		context.Background(),
		con,
		func(
			ctx context.Context,
			dep1 Dependency1,
			dep2 Dependency2,
		) error {
			fmt.Println(dep1)
			fmt.Println(dep2)
			return nil
		},
	); err != nil {
		panic(err)
	}
}
Output:

{<value-1>}
{<value-2>}

func Invoke3

func Invoke3[D1, D2, D3 any](
	ctx context.Context,
	con *Container,
	fn func(context.Context, D1, D2, D3) error,
	options ...InvokeOption,
) error

Invoke3 calls a function with 3 dependencies.

func Invoke4

func Invoke4[D1, D2, D3, D4 any](
	ctx context.Context,
	con *Container,
	fn func(context.Context, D1, D2, D3, D4) error,
	options ...InvokeOption,
) error

Invoke4 calls a function with 4 dependencies.

func Invoke5

func Invoke5[D1, D2, D3, D4, D5 any](
	ctx context.Context,
	con *Container,
	fn func(context.Context, D1, D2, D3, D4, D5) error,
	options ...InvokeOption,
) error

Invoke5 calls a function with 5 dependencies.

func Invoke6

func Invoke6[D1, D2, D3, D4, D5, D6 any](
	ctx context.Context,
	con *Container,
	fn func(context.Context, D1, D2, D3, D4, D5, D6) error,
	options ...InvokeOption,
) error

Invoke6 calls a function with 6 dependencies.

func Invoke7

func Invoke7[D1, D2, D3, D4, D5, D6, D7 any](
	ctx context.Context,
	con *Container,
	fn func(context.Context, D1, D2, D3, D4, D5, D6, D7) error,
	options ...InvokeOption,
) error

Invoke7 calls a function with 7 dependencies.

func Invoke8

func Invoke8[D1, D2, D3, D4, D5, D6, D7, D8 any](
	ctx context.Context,
	con *Container,
	fn func(context.Context, D1, D2, D3, D4, D5, D6, D7, D8) error,
	options ...InvokeOption,
) error

Invoke8 calls a function with 8 dependencies.

func With0

func With0[T any](
	con ContainerAware,
	ctor func(Context) (T, error),
	options ...WithOption,
)

With0 describes how to construct values of type T.

Example
package main

import (
	"fmt"

	"github.com/dogmatiq/imbue"
)

func main() {
	con := imbue.New()
	defer con.Close()

	// Declare a type to use as a dependency within the example.
	type Dependency struct{}

	// Declare a constructor for the Dependency type.
	imbue.With0(
		con,
		func(ctx imbue.Context) (*Dependency, error) {
			return &Dependency{}, nil
		},
	)

	// Print the dependency tree.
	fmt.Println(con)
}
Output:

<container>
└── *imbue_test.Dependency

func With0Grouped added in v0.3.0

func With0Grouped[G Group, T any](
	con ContainerAware,
	ctor func(Context) (T, error),
	options ...WithGroupedOption,
)

With0Grouped describes how to construct grouped values of type T.

G is the group that contains the dependency. T is the type of the dependency.

func With0Named

func With0Named[N Name[T], T any](
	con ContainerAware,
	ctor func(Context) (T, error),
	options ...WithNamedOption,
)

With0Named describes how to construct named values of type T.

N is the name given to the dependency. T is the type of the dependency.

func With1

func With1[T, D any](
	con ContainerAware,
	ctor func(Context, D) (T, error),
	options ...WithOption,
)

With1 describes how to construct values of type T from a single dependency.

Example
package main

import (
	"fmt"

	"github.com/dogmatiq/imbue"
)

func main() {
	con := imbue.New()
	defer con.Close()

	// Declare some types to use as dependencies within the example.
	type UpstreamDependency struct{}
	type Dependency struct {
		Up *UpstreamDependency
	}

	// Declare a constructor for the Dependency type. It depends on the upstream
	// dependency type (which is assumed to be declared elsewhere).
	imbue.With1(
		con,
		func(
			ctx imbue.Context,
			up *UpstreamDependency,
		) (*Dependency, error) {
			return &Dependency{up}, nil
		},
	)

	// Print the dependency tree.
	fmt.Println(con)
}
Output:

<container>
└── *imbue_test.Dependency
    └── *imbue_test.UpstreamDependency

func With1Grouped added in v0.3.0

func With1Grouped[G Group, T, D any](
	con ContainerAware,
	ctor func(Context, D) (T, error),
	options ...WithGroupedOption,
)

With1Grouped describes how to construct grouped values of type T from a single dependency.

G is the group that contains the dependency. T is the type of the dependency.

func With1Named

func With1Named[N Name[T], T, D any](
	con ContainerAware,
	ctor func(Context, D) (T, error),
	options ...WithNamedOption,
)

With1Named describes how to construct named values of type T from a single dependency.

N is the name given to the dependency. T is the type of the dependency.

func With2

func With2[T, D1, D2 any](
	con ContainerAware,
	ctor func(Context, D1, D2) (T, error),
	options ...WithOption,
)

With2 describes how to construct values of type T from 2 dependencies.

Example
package main

import (
	"fmt"

	"github.com/dogmatiq/imbue"
)

func main() {
	con := imbue.New()
	defer con.Close()

	// Declare some types to use as dependencies within the example.
	type UpstreamDependency1 struct{}
	type UpstreamDependency2 struct{}
	type Dependency struct {
		Up1 *UpstreamDependency1
		Up2 *UpstreamDependency2
	}

	// Declare a constructor for the Dependency type. It depends on the two
	// upstream dependency types (which are assumed to be declared elsewhere).
	imbue.With2(
		con,
		func(
			ctx imbue.Context,
			up1 *UpstreamDependency1,
			up2 *UpstreamDependency2,
		) (*Dependency, error) {
			return &Dependency{up1, up2}, nil
		},
	)

	// Print the dependency tree.
	fmt.Println(con)
}
Output:

<container>
└── *imbue_test.Dependency
    ├── *imbue_test.UpstreamDependency1
    └── *imbue_test.UpstreamDependency2

func With2Grouped added in v0.3.0

func With2Grouped[G Group, T, D1, D2 any](
	con ContainerAware,
	ctor func(Context, D1, D2) (T, error),
	options ...WithGroupedOption,
)

With2Grouped describes how to construct grouped values of type T from 2 dependencies.

G is the group that contains the dependency. T is the type of the dependency.

func With2Named

func With2Named[N Name[T], T, D1, D2 any](
	con ContainerAware,
	ctor func(Context, D1, D2) (T, error),
	options ...WithNamedOption,
)

With2Named describes how to construct named values of type T from 2 dependencies.

N is the name given to the dependency. T is the type of the dependency.

func With3

func With3[T, D1, D2, D3 any](
	con ContainerAware,
	ctor func(Context, D1, D2, D3) (T, error),
	options ...WithOption,
)

With3 describes how to construct values of type T from 3 dependencies.

func With3Grouped added in v0.3.0

func With3Grouped[G Group, T, D1, D2, D3 any](
	con ContainerAware,
	ctor func(Context, D1, D2, D3) (T, error),
	options ...WithGroupedOption,
)

With3Grouped describes how to construct grouped values of type T from 3 dependencies.

G is the group that contains the dependency. T is the type of the dependency.

func With3Named

func With3Named[N Name[T], T, D1, D2, D3 any](
	con ContainerAware,
	ctor func(Context, D1, D2, D3) (T, error),
	options ...WithNamedOption,
)

With3Named describes how to construct named values of type T from 3 dependencies.

N is the name given to the dependency. T is the type of the dependency.

func With4

func With4[T, D1, D2, D3, D4 any](
	con ContainerAware,
	ctor func(Context, D1, D2, D3, D4) (T, error),
	options ...WithOption,
)

With4 describes how to construct values of type T from 4 dependencies.

func With4Grouped added in v0.3.0

func With4Grouped[G Group, T, D1, D2, D3, D4 any](
	con ContainerAware,
	ctor func(Context, D1, D2, D3, D4) (T, error),
	options ...WithGroupedOption,
)

With4Grouped describes how to construct grouped values of type T from 4 dependencies.

G is the group that contains the dependency. T is the type of the dependency.

func With4Named

func With4Named[N Name[T], T, D1, D2, D3, D4 any](
	con ContainerAware,
	ctor func(Context, D1, D2, D3, D4) (T, error),
	options ...WithNamedOption,
)

With4Named describes how to construct named values of type T from 4 dependencies.

N is the name given to the dependency. T is the type of the dependency.

func With5

func With5[T, D1, D2, D3, D4, D5 any](
	con ContainerAware,
	ctor func(Context, D1, D2, D3, D4, D5) (T, error),
	options ...WithOption,
)

With5 describes how to construct values of type T from 5 dependencies.

func With5Grouped added in v0.3.0

func With5Grouped[G Group, T, D1, D2, D3, D4, D5 any](
	con ContainerAware,
	ctor func(Context, D1, D2, D3, D4, D5) (T, error),
	options ...WithGroupedOption,
)

With5Grouped describes how to construct grouped values of type T from 5 dependencies.

G is the group that contains the dependency. T is the type of the dependency.

func With5Named

func With5Named[N Name[T], T, D1, D2, D3, D4, D5 any](
	con ContainerAware,
	ctor func(Context, D1, D2, D3, D4, D5) (T, error),
	options ...WithNamedOption,
)

With5Named describes how to construct named values of type T from 5 dependencies.

N is the name given to the dependency. T is the type of the dependency.

func With6

func With6[T, D1, D2, D3, D4, D5, D6 any](
	con ContainerAware,
	ctor func(Context, D1, D2, D3, D4, D5, D6) (T, error),
	options ...WithOption,
)

With6 describes how to construct values of type T from 6 dependencies.

func With6Grouped added in v0.3.0

func With6Grouped[G Group, T, D1, D2, D3, D4, D5, D6 any](
	con ContainerAware,
	ctor func(Context, D1, D2, D3, D4, D5, D6) (T, error),
	options ...WithGroupedOption,
)

With6Grouped describes how to construct grouped values of type T from 6 dependencies.

G is the group that contains the dependency. T is the type of the dependency.

func With6Named

func With6Named[N Name[T], T, D1, D2, D3, D4, D5, D6 any](
	con ContainerAware,
	ctor func(Context, D1, D2, D3, D4, D5, D6) (T, error),
	options ...WithNamedOption,
)

With6Named describes how to construct named values of type T from 6 dependencies.

N is the name given to the dependency. T is the type of the dependency.

func With7

func With7[T, D1, D2, D3, D4, D5, D6, D7 any](
	con ContainerAware,
	ctor func(Context, D1, D2, D3, D4, D5, D6, D7) (T, error),
	options ...WithOption,
)

With7 describes how to construct values of type T from 7 dependencies.

func With7Grouped added in v0.3.0

func With7Grouped[G Group, T, D1, D2, D3, D4, D5, D6, D7 any](
	con ContainerAware,
	ctor func(Context, D1, D2, D3, D4, D5, D6, D7) (T, error),
	options ...WithGroupedOption,
)

With7Grouped describes how to construct grouped values of type T from 7 dependencies.

G is the group that contains the dependency. T is the type of the dependency.

func With7Named

func With7Named[N Name[T], T, D1, D2, D3, D4, D5, D6, D7 any](
	con ContainerAware,
	ctor func(Context, D1, D2, D3, D4, D5, D6, D7) (T, error),
	options ...WithNamedOption,
)

With7Named describes how to construct named values of type T from 7 dependencies.

N is the name given to the dependency. T is the type of the dependency.

func With8

func With8[T, D1, D2, D3, D4, D5, D6, D7, D8 any](
	con ContainerAware,
	ctor func(Context, D1, D2, D3, D4, D5, D6, D7, D8) (T, error),
	options ...WithOption,
)

With8 describes how to construct values of type T from 8 dependencies.

func With8Grouped added in v0.3.0

func With8Grouped[G Group, T, D1, D2, D3, D4, D5, D6, D7, D8 any](
	con ContainerAware,
	ctor func(Context, D1, D2, D3, D4, D5, D6, D7, D8) (T, error),
	options ...WithGroupedOption,
)

With8Grouped describes how to construct grouped values of type T from 8 dependencies.

G is the group that contains the dependency. T is the type of the dependency.

func With8Named

func With8Named[N Name[T], T, D1, D2, D3, D4, D5, D6, D7, D8 any](
	con ContainerAware,
	ctor func(Context, D1, D2, D3, D4, D5, D6, D7, D8) (T, error),
	options ...WithNamedOption,
)

With8Named describes how to construct named values of type T from 8 dependencies.

N is the name given to the dependency. T is the type of the dependency.

Types

type ByName

type ByName[N Name[T], T any] struct {
	// contains filtered or unexported fields
}

ByName requests a dependency on a type with a specific name.

It is used as a parameter type within user-defined functions passed to WithX(), DecorateX() and InvokeX() to request a dependency of type T that is named N.

Example
package main

import (
	"context"
	"fmt"

	"github.com/dogmatiq/imbue"
)

// Color is a type that represents a color.
type Color string

type (
	// Foreground is a name for a color.
	Foreground imbue.Name[Color]

	// Background is a name for a color.
	Background imbue.Name[Color]
)

func main() {
	con := imbue.New()
	defer con.Close()

	// Declare a constructor for a Color named Foreground.
	imbue.With0Named[Foreground](
		con,
		func(
			ctx imbue.Context,
		) (Color, error) {
			return "<black>", nil
		},
	)

	// Declare a constructor for a Color named Background.
	imbue.With0Named[Background](
		con,
		func(
			ctx imbue.Context,
		) (Color, error) {
			return "<white>", nil
		},
	)

	// Invoke a function that depends on both the Foreground and Background
	// colors.
	err := imbue.Invoke2(
		context.Background(),
		con,
		func(
			ctx context.Context,
			fg imbue.ByName[Foreground, Color],
			bg imbue.ByName[Background, Color],
		) error {
			// Named dependencies have a Name() and Value() methods which return
			// the name and value of the dependency.
			fmt.Println(fg.Name(), "=", fg.Value())
			fmt.Println(bg.Name(), "=", bg.Value())
			return nil
		},
	)
	if err != nil {
		panic(err)
	}
}
Output:

Foreground = <black>
Background = <white>

func (ByName[N, T]) Name added in v0.5.0

func (v ByName[N, T]) Name() string

Name returns the name given to the dependency.

func (ByName[N, T]) Value added in v0.2.0

func (v ByName[N, T]) Value() T

Value returns the dependency value.

type Catalog added in v0.7.0

type Catalog struct {
	// contains filtered or unexported fields
}

Catalog is a reusable set of constructors and decorators that can be added to different containers.

It implements the ContainerAware interface, so it can be passed to the WithX(), WithXNamed(), WithXGrouped() and DecorateX() functions instead of a container.

Example
package main

import (
	"context"
	"fmt"

	"github.com/dogmatiq/imbue"
)

func main() {
	cat := imbue.NewCatalog()

	// Declare a type to use as a dependency within the example.
	type Dependency struct {
		Count int
	}

	// Keep track of how many times we have constructed the dependency.
	count := 0

	// Declare a constructor for the Dependency type within the catalog.
	imbue.With0(
		cat,
		func(
			ctx imbue.Context,
		) (*Dependency, error) {
			count++
			return &Dependency{
				Count: count,
			}, nil
		},
	)

	// Create a new container from the catalog.
	con1 := imbue.New(imbue.WithCatalog(cat))
	defer con1.Close()

	imbue.Invoke1(
		context.Background(),
		con1,
		func(
			ctx context.Context,
			d *Dependency,
		) error {
			fmt.Println("count is", d.Count)
			return nil
		},
	)

	// Create a second container from the same catalog.
	con2 := imbue.New(imbue.WithCatalog(cat))
	defer con2.Close()

	imbue.Invoke1(
		context.Background(),
		con2,
		func(
			ctx context.Context,
			d *Dependency,
		) error {
			fmt.Println("count is", d.Count)
			return nil
		},
	)

}
Output:

count is 1
count is 2

func NewCatalog added in v0.7.0

func NewCatalog(options ...CatalogOption) *Catalog

NewCatalog creturns a new catalog.

type CatalogOption added in v0.7.0

type CatalogOption interface {
	// contains filtered or unexported methods
}

CatalogOption is an option that changes the behavior of a Catalog.

type Container

type Container struct {
	// contains filtered or unexported fields
}

Container is a dependency injection container.

func New

func New(options ...ContainerOption) *Container

New returns a new, empty container.

func (*Container) Close

func (c *Container) Close() error

Close closes the container, calling any deferred functions registered during construction of dependencies.

func (*Container) String added in v0.3.0

func (c *Container) String() string

String returns a string representation of the dependency tree.

func (*Container) WaitGroup added in v0.6.2

func (c *Container) WaitGroup(ctx context.Context) *WaitGroup

WaitGroup returns a new WaitGroup that is bound to this container.

Example
package main

import (
	"context"
	"fmt"
	"time"

	"github.com/dogmatiq/imbue"
)

func main() {
	con := imbue.New()
	defer con.Close()

	// Declare some types to use as dependencies within the example.
	type Dependency1 struct{ Value string }
	type Dependency2 struct{ Value string }

	// Declare a constructor for the Dependency1 type.
	imbue.With0(
		con,
		func(ctx imbue.Context) (Dependency1, error) {
			return Dependency1{"<value-1>"}, nil
		},
	)

	// Declare a constructor for the Dependency2 type.
	imbue.With0(
		con,
		func(ctx imbue.Context) (Dependency2, error) {
			return Dependency2{"<value-2>"}, nil
		},
	)

	// Create a wait group that is bound to the container.
	g := con.WaitGroup(context.Background())

	// Start some goroutines that depend on the dependencies.
	imbue.Go1(
		g,
		func(
			ctx context.Context,
			dep Dependency1,
		) error {
			fmt.Println(dep)
			return nil
		},
	)
	imbue.Go1(
		g,
		func(
			ctx context.Context,
			dep Dependency2,
		) error {
			time.Sleep(10 * time.Millisecond)
			fmt.Println(dep)
			return nil
		},
	)

	// Wait for both goroutines to finish.
	if err := g.Wait(); err != nil {
		panic(err)
	}

}
Output:

{<value-1>}
{<value-2>}

type ContainerAware added in v0.7.0

type ContainerAware interface {
	// contains filtered or unexported methods
}

ContainerAware is an interface for types that can operate on a container.

type ContainerOption added in v0.7.0

type ContainerOption interface {
	// contains filtered or unexported methods
}

ContainerOption is an option that changes the behavior of a container or how it is constructed.

func WithCatalog added in v0.7.0

func WithCatalog(cat *Catalog) ContainerOption

WithCatalog is a ContainerOption that adds the declarations in the catalog to the container.

type Context

type Context interface {
	context.Context

	// Defer registers a function to be invoked when the container is closed.
	Defer(fn func() error)
}

Context is an extended version of the standard context.Context interface that is used when constructing and decorating dependencies.

type DecorateOption added in v0.4.0

type DecorateOption interface {
	// contains filtered or unexported methods
}

DecorateOption is an option that changes the behavior of a call to DecorateX().

type FromGroup added in v0.3.0

type FromGroup[G Group, T any] struct {
	// contains filtered or unexported fields
}

FromGroup declares a dependency on a type within a specific group.

It is used as a parameter type within user-defined functions passed to WithX(), DecorateX(), and InvokeX() to request of type T that is within the group G.

Example
package main

import (
	"context"
	"fmt"

	"github.com/dogmatiq/imbue"
)

// Connection is a connection to a remote service.
type Connection string

// Client is an API client that uses a connection to a remote service.
type Client struct {
	Conn Connection
}

type (
	// ServiceA is a group for the dependencies related to service A.
	ServiceA imbue.Group

	// ServiceB is a group for the dependencies related to service B.
	ServiceB imbue.Group
)

func main() {
	con := imbue.New()
	defer con.Close()

	// Declare a constructor for the Connection to ServiceA.
	imbue.With0Grouped[ServiceA](
		con,
		func(
			ctx imbue.Context,
		) (Connection, error) {
			return "<connection-a>", nil
		},
	)

	// Declare a constructor for the API Client for ServiceA.
	imbue.With1Grouped[ServiceA](
		con,
		func(
			ctx imbue.Context,
			conn imbue.FromGroup[ServiceA, Connection],
		) (Client, error) {
			return Client{conn.Value()}, nil
		},
	)

	// Declare a constructor for the Connection to ServiceB.
	imbue.With0Grouped[ServiceB](
		con,
		func(
			ctx imbue.Context,
		) (Connection, error) {
			return "<connection-b>", nil
		},
	)

	// Declare a constructor for the API Client for ServiceB.
	imbue.With1Grouped[ServiceB](
		con,
		func(
			ctx imbue.Context,
			conn imbue.FromGroup[ServiceB, Connection],
		) (Client, error) {
			return Client{conn.Value()}, nil
		},
	)

	// Invoke a function that depends on both the Foreground and Background
	// colors.
	err := imbue.Invoke2(
		context.Background(),
		con,
		func(
			ctx context.Context,
			clientA imbue.FromGroup[ServiceA, Client],
			clientB imbue.FromGroup[ServiceB, Client],
		) error {
			// Grouped dependencies have a Group() method, which returns the
			// name of the group, and a Value() method which returns the actual
			// dependency value.
			fmt.Println(clientA.Group(), "=", clientA.Value())
			fmt.Println(clientB.Group(), "=", clientB.Value())
			return nil
		},
	)
	if err != nil {
		panic(err)
	}
}
Output:

ServiceA = {<connection-a>}
ServiceB = {<connection-b>}

func (FromGroup[G, T]) Group added in v0.5.0

func (v FromGroup[G, T]) Group() string

Group returns the name given to the group.

func (FromGroup[G, T]) Value added in v0.3.0

func (v FromGroup[G, T]) Value() T

Value returns the dependency value.

type Group added in v0.3.0

type Group interface {
	// contains filtered or unexported methods
}

Group is a constraint for a type that identifies a group of dependencies.

Groups are used to group multiple dependencies of different types that are related in some way.

Groups are defined by declaring a named type that uses imbue.Group as its underlying type.

type InvokeOption added in v0.3.0

type InvokeOption interface {
	// contains filtered or unexported methods
}

InvokeOption is an option that changes the behavior of a call to InvokeX().

type Name

type Name[T any] interface {
	// contains filtered or unexported methods
}

Name is a constraint for a type that identifies a named dependency.

Names are used to distinguish between multiple dependencies of the same type.

Names are defined by declaring a type that uses imbue.Name[T] as its underlying type, where T is the type of the dependency being named.

type Optional added in v0.5.0

type Optional[T any] struct {
	// contains filtered or unexported fields
}

Optional represents an optional dependency of type T.

Example
package main

import (
	"context"
	"fmt"

	"github.com/dogmatiq/imbue"
)

func main() {
	con := imbue.New()
	defer con.Close()

	// Declare a type to use as a dependency within the example.
	type Dependency struct {
		Value string
	}

	// Declare a constructor for Dependency, but have it return an error.
	imbue.With0(
		con,
		func(ctx imbue.Context) (Dependency, error) {
			return Dependency{"<value>"}, nil
		},
	)

	// Invoke a function that optionally depends on the Dependency type.
	if err := imbue.Invoke1(
		context.Background(),
		con,
		func(
			ctx context.Context,
			dep imbue.Optional[Dependency],
		) error {
			v, err := dep.Value()
			if err != nil {
				fmt.Println("dependency is unavailable:", err)
			} else {
				fmt.Println("dependency is available:", v)
			}

			return nil
		},
	); err != nil {
		panic(err)
	}
}
Output:

dependency is available: {<value>}
Example (ConstructorNotDeclared)
package main

import (
	"context"
	"fmt"

	"github.com/dogmatiq/imbue"
)

func main() {
	con := imbue.New()
	defer con.Close()

	// Declare a type to use as a dependency within the example.
	// Note that we don't actually declare a constructor for this type.
	type Dependency struct {
		Value string
	}

	// Invoke a function that optionally depends on the Dependency type.
	if err := imbue.Invoke1(
		context.Background(),
		con,
		func(
			ctx context.Context,
			dep imbue.Optional[Dependency],
		) error {
			v, err := dep.Value()
			if err != nil {
				fmt.Println("dependency is unavailable:", err)
			} else {
				fmt.Println("dependency is available:", v)
			}

			return nil
		},
	); err != nil {
		panic(err)
	}
}
Output:

dependency is unavailable: no constructor is declared for imbue_test.Dependency
Example (FailingConstructor)
package main

import (
	"context"
	"errors"
	"fmt"

	"github.com/dogmatiq/imbue"
)

func main() {
	con := imbue.New()
	defer con.Close()

	// Declare a type to use as a dependency within the example.
	type Dependency struct {
		Value string
	}

	// Declare a constructor for Dependency, but have it return an error.
	imbue.With0(
		con,
		func(ctx imbue.Context) (Dependency, error) {
			return Dependency{}, errors.New("<error>")
		},
	)

	// Invoke a function that optionally depends on the Dependency type.
	if err := imbue.Invoke1(
		context.Background(),
		con,
		func(
			ctx context.Context,
			dep imbue.Optional[Dependency],
		) error {
			v, err := dep.Value()
			if err != nil {
				fmt.Println("dependency is unavailable:", err)
			} else {
				fmt.Println("dependency is available:", v)
			}

			return nil
		},
	); err != nil {
		panic(err)
	}
}
Output:

dependency is unavailable: imbue_test.Dependency constructor (optionalexample_test.go:62) failed: <error>

func (Optional[T]) Value added in v0.5.0

func (v Optional[T]) Value() (T, error)

Value returns the dependency value if it is available; otherwise, it returns a non-nil error.

A dependency is considered unavailable if it does not have a constructor declared, or if that constructor returns an error.

type WaitGroup added in v0.6.2

type WaitGroup struct {
	// contains filtered or unexported fields
}

A WaitGroup is a collection of goroutines working on subtasks that are part of the same overall task, each of which has dependencies provided by a container.

func (*WaitGroup) Wait added in v0.6.2

func (g *WaitGroup) Wait() error

Wait blocks until all function calls from the Go method have returned, then returns the first non-nil error (if any) from them.

type WithGroupedOption added in v0.3.0

type WithGroupedOption interface {
	// contains filtered or unexported methods
}

WithGroupedOption is an option that changes the behavior of a call to WithXGrouped().

type WithNamedOption added in v0.3.0

type WithNamedOption interface {
	// contains filtered or unexported methods
}

WithNamedOption is an option that changes the behavior of a call to WithXNamed().

type WithOption added in v0.3.0

type WithOption interface {
	// contains filtered or unexported methods
}

WithOption is an option that changes the behavior of a call to WithX().

Directories

Path Synopsis
internal
generate/generator
Package generator generates functions for each arity supported by Imbue's public API.
Package generator generates functions for each arity supported by Imbue's public API.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL