-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
28 changed files
with
2,090 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
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: 85% # 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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
name: Go | ||
|
||
on: | ||
push: | ||
branches: ['*'] | ||
tags: ['v*'] | ||
pull_request: | ||
branches: ['*'] | ||
|
||
jobs: | ||
|
||
build: | ||
runs-on: ubuntu-latest | ||
strategy: | ||
matrix: | ||
go: ["1.18.x", "1.22.x"] | ||
include: | ||
- go: 1.22.x | ||
latest: true | ||
|
||
steps: | ||
- name: Setup Go | ||
uses: actions/setup-go@v4 | ||
with: | ||
go-version: ${{ matrix.go }} | ||
|
||
- name: Checkout code | ||
uses: actions/checkout@v3 | ||
|
||
- name: Load cached dependencies | ||
uses: actions/cache@v3 | ||
with: | ||
path: ~/go/pkg/mod | ||
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} | ||
restore-keys: | | ||
${{ runner.os }}-go- | ||
- name: Download Dependencies | ||
run: make prepare | ||
|
||
- name: Lint | ||
run: make lint | ||
|
||
- name: Test | ||
run: make cover | ||
|
||
- name: Upload coverage to codecov.io | ||
uses: codecov/codecov-action@v3 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
# Binaries for programs and plugins | ||
*.exe | ||
*.exe~ | ||
*.dll | ||
*.so | ||
*.dylib | ||
|
||
# Test | ||
*.test | ||
*.out | ||
|
||
# Dependency | ||
vendor/ | ||
|
||
# Goland, vscode, OS | ||
.idea | ||
.vscode | ||
.DS_Store |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
linters-settings: | ||
funlen: | ||
lines: 100 | ||
statements: 80 | ||
gci: | ||
sections: | ||
- standard | ||
- default | ||
- prefix(github.com/tiendc/autowire) | ||
gocyclo: | ||
min-complexity: 20 | ||
goimports: | ||
local-prefixes: github.com/golangci/golangci-lint | ||
lll: | ||
line-length: 120 | ||
misspell: | ||
locale: US | ||
|
||
linters: | ||
enable: | ||
- bodyclose | ||
- contextcheck | ||
- dogsled | ||
- errcheck | ||
- errname | ||
- errorlint | ||
- exhaustive | ||
- exportloopref | ||
- forbidigo | ||
- forcetypeassert | ||
- funlen | ||
- gci | ||
- gocognit | ||
- goconst | ||
- gocritic | ||
- gocyclo | ||
- goerr113 | ||
- gofmt | ||
- goimports | ||
- gomnd | ||
- gosec | ||
- gosimple | ||
- govet | ||
- ineffassign | ||
- lll | ||
- misspell | ||
- nakedret | ||
- nestif | ||
- nilerr | ||
- rowserrcheck | ||
- staticcheck | ||
- stylecheck | ||
- typecheck | ||
- unconvert | ||
- unparam | ||
- unused | ||
- whitespace | ||
|
||
issues: | ||
exclude-rules: | ||
- path: _test\.go | ||
linters: | ||
- funlen | ||
- contextcheck | ||
- staticcheck | ||
- stylecheck | ||
- gocyclo | ||
- gocognit | ||
- goerr113 | ||
- forcetypeassert | ||
- wrapcheck | ||
- gomnd | ||
- errorlint | ||
- unused |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
all: lint test | ||
|
||
prepare: | ||
@curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(shell go env GOPATH)/bin v1.54.2 | ||
|
||
build: | ||
@go build -v ./... | ||
|
||
test: | ||
@go test -cover -v ./... | ||
|
||
cover: | ||
@go test -race -coverprofile=cover.out -coverpkg=./... ./... | ||
@go tool cover -html=cover.out -o cover.html | ||
|
||
lint: | ||
golangci-lint --timeout=5m0s run -v ./... | ||
|
||
bench: | ||
go test -benchmem -count 100 -bench . | ||
|
||
mod: | ||
go mod tidy && go mod vendor |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,121 @@ | ||
[![Go Version][gover-img]][gover] [![GoDoc][doc-img]][doc] [![Build Status][ci-img]][ci] [![Coverage Status][cov-img]][cov] [![GoReport][rpt-img]][rpt] | ||
|
||
# Dependency injection for Go 1.18+ using Generics and reflection | ||
|
||
## Functionalities | ||
|
||
- Automatically wiring/injecting objects on creation. | ||
- Easy to use (see Usages below). | ||
- `Shared mode` by default that creates only one instance for a type. This option is configurable. | ||
- Ability to overwrite objects of specific types on building which is convenient for unit testing. | ||
- No code generation. | ||
|
||
## Installation | ||
|
||
```shell | ||
go get github.com/tiendc/autowire | ||
``` | ||
|
||
## Usage | ||
|
||
### General usage | ||
|
||
```go | ||
// Suppose you have ServiceA and a creator function | ||
type ServiceA interface {} | ||
func NewServiceA(srvB ServiceB, repoX RepoX) ServiceA { | ||
return <ServiceA-instance> | ||
} | ||
|
||
// and ServiceB | ||
type ServiceB interface {} | ||
func NewServiceB(ctx context.Context, repoX RepoX, repoY RepoY) (ServiceB, error) { | ||
return <ServiceB-instance>, nil | ||
} | ||
|
||
// and RepoX | ||
type RepoX interface {} | ||
func NewRepoX(s3Client S3Client) (RepoX, error) { | ||
return <RepoX-instance>, nil | ||
} | ||
|
||
// and RepoY | ||
type RepoY interface {} | ||
func NewRepoY(redisClient RedisClient) RepoY { | ||
return <RepoY-instance>, nil | ||
} | ||
|
||
// and a struct provider | ||
type ProviderStruct struct { | ||
RedisClient RedisClient | ||
S3Client S3Client | ||
} | ||
|
||
var ( | ||
// init the struct value | ||
providerStruct = &ProviderStruct{...} | ||
|
||
// create a container with passing providers | ||
container = MustNewContainer([]any{ | ||
// Service providers | ||
NewServiceA, | ||
NewServiceB, | ||
// Repo providers | ||
NewRepoX, | ||
NewRepoY, | ||
// Struct provider (must be a pointer) | ||
providerStruct, | ||
}) | ||
) | ||
|
||
func main() { | ||
// Update some values of the struct provider | ||
providerStruct.RedisClient = newRedisClient() | ||
providerStruct.S3Client = newS3Client() | ||
|
||
// Create ServiceA | ||
serviceA, err := autowire.BuildWithCtx[ServiceA](ctx, container) | ||
// Create RepoX | ||
repoX, err := autowire.Build[RepoX](container) | ||
} | ||
``` | ||
|
||
### Non-shared mode | ||
|
||
```go | ||
// Set sharedMode when create a container | ||
container = MustNewContainer([]any{ | ||
// your providers | ||
}, SetSharedMode(false)) | ||
|
||
// Activate non-shared mode inline for a specific build only | ||
serviceA, err := Build[ServiceA](container, NonSharedMode()) | ||
``` | ||
|
||
### Overwrite values of specific types | ||
|
||
This is convenient in unit testing to overwrite specific types only. | ||
|
||
```go | ||
// In unit testing, you may want to overwrite `RepoX` and `RepoY` with fake instances | ||
serviceA, err := Build[ServiceA](container, ProviderOverwrite[RepoX](fakeRepoX), ProviderOverwrite[RepoY](fakeRepoY)) | ||
``` | ||
|
||
## Contributing | ||
|
||
- You are welcome to make pull requests for new functions and bug fixes. | ||
|
||
## License | ||
|
||
- [MIT License](LICENSE) | ||
|
||
[doc-img]: https://pkg.go.dev/badge/github.com/tiendc/autowire | ||
[doc]: https://pkg.go.dev/github.com/tiendc/autowire | ||
[gover-img]: https://img.shields.io/badge/Go-%3E%3D%201.18-blue | ||
[gover]: https://img.shields.io/badge/Go-%3E%3D%201.18-blue | ||
[ci-img]: https://github.com/tiendc/autowire/actions/workflows/go.yml/badge.svg | ||
[ci]: https://github.com/tiendc/autowire/actions/workflows/go.yml | ||
[cov-img]: https://codecov.io/gh/tiendc/autowire/branch/main/graph/badge.svg | ||
[cov]: https://codecov.io/gh/tiendc/autowire | ||
[rpt-img]: https://goreportcard.com/badge/github.com/tiendc/autowire | ||
[rpt]: https://goreportcard.com/report/github.com/tiendc/autowire |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
package autowire | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"reflect" | ||
) | ||
|
||
// Build builds object for the specified type within a container | ||
func Build[T any](c Container, opts ...ContextOption) (value T, err error) { | ||
targetType := reflect.TypeFor[T]() | ||
|
||
val, err := c.Build(targetType, opts...) | ||
if err != nil { | ||
return value, err | ||
} | ||
|
||
value, ok := val.Interface().(T) | ||
if !ok { // this should never happen | ||
return value, fmt.Errorf("%w: unable to cast result as type '%v'", ErrTypeCast, targetType) | ||
} | ||
|
||
return value, nil | ||
} | ||
|
||
// BuildWithCtx builds object for the specified type within a container. | ||
// This function will pass the specified context object to every provider that requires a context. | ||
func BuildWithCtx[T any](ctx context.Context, c Container, opts ...ContextOption) (value T, err error) { | ||
targetType := reflect.TypeFor[T]() | ||
|
||
val, err := c.BuildWithCtx(ctx, targetType, opts...) | ||
if err != nil { | ||
return value, err | ||
} | ||
|
||
value, ok := val.Interface().(T) | ||
if !ok { // this should never happen | ||
return value, fmt.Errorf("%w: unable to cast result as type '%v'", ErrTypeCast, targetType) | ||
} | ||
|
||
return value, nil | ||
} | ||
|
||
// Get gets object of a type within a container. | ||
// If no object is created for the type or `sharedMode` is `false`, ErrNotFound is returned. | ||
func Get[T any](c Container) (value T, err error) { | ||
targetType := reflect.TypeFor[T]() | ||
|
||
val, err := c.Get(targetType) | ||
if err != nil { | ||
return value, err | ||
} | ||
|
||
value, ok := val.Interface().(T) | ||
if !ok { // this should never happen | ||
return value, fmt.Errorf("%w: unable to cast result as type '%v'", ErrTypeCast, targetType) | ||
} | ||
|
||
return value, nil | ||
} | ||
|
||
// Resolve builds dependency graph for the specified type within a container | ||
func Resolve[T any](c Container) (DependencyGraph, error) { | ||
return c.Resolve(reflect.TypeFor[T]()) | ||
} |
Oops, something went wrong.