Skip to content

Commit

Permalink
Merge pull request #2 from Otoru/feature/merge
Browse files Browse the repository at this point in the history
Merge Functionality 🚀
  • Loading branch information
Otoru authored Sep 8, 2023
2 parents 41ca1fb + 27ecc12 commit 5e86e85
Show file tree
Hide file tree
Showing 5 changed files with 99 additions and 5 deletions.
31 changes: 31 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
# 🌩 Zeus - Simple Dependency Injection Container

[![GoDoc](https://pkg.go.dev/badge/otoru/zeus)](https://pkg.go.dev/github.com/otoru/zeus)
![GitHub](https://img.shields.io/github/license/otoru/zeus)
![GitHub go.mod Go version (subdirectory of monorepo)](https://img.shields.io/github/go-mod/go-version/otoru/zeus)
[![Go Report Card](https://goreportcard.com/badge/github.com/otoru/zeus)](https://goreportcard.com/report/github.com/otoru/zeus)
[![codecov](https://codecov.io/gh/Otoru/zeus/graph/badge.svg?token=Yfkyp5NZsY)](https://codecov.io/gh/Otoru/zeus)

Zeus is a sleek and efficient dependency injection container for Go. Easily register "factories" (functions that create instances of types) and let zeus resolve those dependencies at runtime.
Expand All @@ -26,6 +28,35 @@ Zeus detects and reports cycles in your dependencies to prevent runtime errors.

Zeus supports lifecycle hooks, allowing you to execute functions at the start and end of your application. This is especially useful for setups and teardowns, like establishing a database connection or gracefully shutting down services.

### 🔄 Merging Containers

Zeus now supports merging two containers together using the Merge method. This is especially useful when you have modularized your application and want to combine dependencies from different modules.

#### How to Use

1. Create two separate containers.
2. Add factories to both containers.
3. Use the Merge method to combine the factories of one container into another.

#### Example

```go
containerA := zeus.New()
containerB := zeus.New()

containerA.Provide(func() string { return "Hello" })
containerB.Provide(func() int { return 42 })

err := containerA.Merge(containerB)
if err != nil {
// Handle merge error
}
```

#### Note

If a factory from the merging container conflicts with an existing factory in the main container, and they are not identical, a FactoryAlreadyProvidedError will be returned. This ensures that you don't accidentally overwrite existing dependencies.

## 🚀 Getting Started

### Installation
Expand Down
32 changes: 31 additions & 1 deletion container.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ package zeus

import (
"reflect"
"slices"

"github.com/otoru/zeus/errs"
"github.com/otoru/zeus/hooks"
"golang.org/x/exp/slices"
)

// Container holds the registered factories for dependency resolution.
Expand Down Expand Up @@ -178,3 +178,33 @@ func (c *Container) Run(fn interface{}) error {

return errorSet.Result()
}

// Merge combines the factories of another container into the current container.
// If a factory from the other container conflicts with an existing factory in the current container,
// and they are not identical, a FactoryAlreadyProvidedError is returned.
//
// Example:
//
// containerA := New()
// containerB := New()
//
// containerA.Provide(func() string { return "Hello" })
// containerB.Provide(func() int { return 42 })
//
// err := containerA.Merge(containerB)
// if err != nil {
// // Handle merge error
// }
func (c *Container) Merge(other *Container) error {
for t, factory := range other.providers {
if existingFactory, exists := c.providers[t]; exists {
if !reflect.DeepEqual(existingFactory, factory) {
return errs.FactoryAlreadyProvidedError{TypeName: t.Name()}
}
continue
}

c.providers[t] = factory
}
return nil
}
37 changes: 37 additions & 0 deletions container_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -294,4 +294,41 @@ func TestContainer(t *testing.T) {
assert.ErrorContains(t, err, "stop error")
})
})

t.Run("Merge", func(t *testing.T) {
t.Run("Merge without conflicts", func(t *testing.T) {
containerA := New()
containerB := New()

containerA.Provide(func() string { return "Hello" })
containerB.Provide(func() int { return 42 })

err := containerA.Merge(containerB)
assert.NilError(t, err)
})

t.Run("Merge with identical factories", func(t *testing.T) {
containerA := New()
containerB := New()

factory := func() string { return "Hello" }

containerA.Provide(factory)
containerB.Provide(factory)

err := containerA.Merge(containerB)
assert.NilError(t, err)
})

t.Run("Merge with conflicting factories", func(t *testing.T) {
containerA := New()
containerB := New()

containerA.Provide(func() string { return "Hello" })
containerB.Provide(func() string { return "World" })

err := containerA.Merge(containerB)
assert.ErrorIs(t, err, errs.FactoryAlreadyProvidedError{TypeName: "string"})
})
})
}
2 changes: 0 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@ module github.com/otoru/zeus

go 1.21.0

require golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63

require (
github.com/google/go-cmp v0.5.9 // indirect
gotest.tools/v3 v3.5.0
Expand Down
2 changes: 0 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63 h1:m64FZMko/V45gv0bNmrNYoDEq8U5YUhetc9cBWKS1TQ=
golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63/go.mod h1:0v4NqG35kSWCMzLaMeX+IQrlSnVE/bqGSyC2cz/9Le8=
gotest.tools/v3 v3.5.0 h1:Ljk6PdHdOhAb5aDMWXjDLMMhph+BpztA4v1QdqEW2eY=
gotest.tools/v3 v3.5.0/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU=

0 comments on commit 5e86e85

Please sign in to comment.