2023-11-25 16:56:39 +01:00
|
|
|
// Copyright (c) 2023 IBM Corp.
|
|
|
|
// All rights reserved.
|
|
|
|
//
|
|
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
// you may not use this file except in compliance with the License.
|
|
|
|
// You may obtain a copy of the License at
|
|
|
|
//
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
//
|
|
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
// See the License for the specific language governing permissions and
|
|
|
|
// limitations under the License.
|
|
|
|
|
|
|
|
package di
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"strconv"
|
|
|
|
"sync/atomic"
|
|
|
|
|
|
|
|
DIE "github.com/IBM/fp-go/di/erasure"
|
|
|
|
E "github.com/IBM/fp-go/either"
|
|
|
|
IO "github.com/IBM/fp-go/io"
|
|
|
|
IOE "github.com/IBM/fp-go/ioeither"
|
|
|
|
IOO "github.com/IBM/fp-go/iooption"
|
|
|
|
O "github.com/IBM/fp-go/option"
|
|
|
|
)
|
|
|
|
|
|
|
|
// Dependency describes the relationship to a service, that has a type and
|
|
|
|
// a behaviour such as required, option or lazy
|
|
|
|
type Dependency[T any] interface {
|
|
|
|
DIE.Dependency
|
|
|
|
// Unerase converts a value with erased type signature into a strongly typed value
|
|
|
|
Unerase(val any) E.Either[error, T]
|
|
|
|
}
|
|
|
|
|
|
|
|
// InjectionToken uniquely identifies a dependency by giving it an Id, Type and name
|
|
|
|
type InjectionToken[T any] interface {
|
|
|
|
Dependency[T]
|
|
|
|
// Identity idenifies this dependency as a mandatory, required dependency, it will be resolved eagerly and injected as `T`.
|
|
|
|
// If the dependency cannot be resolved, the resolution process fails
|
|
|
|
Identity() Dependency[T]
|
2023-12-19 12:18:07 +01:00
|
|
|
// Option identifies this dependency as optional, it will be resolved eagerly and injected as [O.Option[T]].
|
|
|
|
// If the dependency cannot be resolved, the resolution process continues and the dependency is represented as [O.None[T]]
|
2023-11-25 16:56:39 +01:00
|
|
|
Option() Dependency[O.Option[T]]
|
2023-12-19 12:18:07 +01:00
|
|
|
// IOEither identifies this dependency as mandatory but it will be resolved lazily as a [IOE.IOEither[error, T]]. This
|
2023-11-25 16:56:39 +01:00
|
|
|
// value is memoized to make sure the dependency is a singleton.
|
|
|
|
// If the dependency cannot be resolved, the resolution process fails
|
|
|
|
IOEither() Dependency[IOE.IOEither[error, T]]
|
2023-12-19 12:18:07 +01:00
|
|
|
// IOOption identifies this dependency as optional but it will be resolved lazily as a [IOO.IOOption[T]]. This
|
2023-11-25 16:56:39 +01:00
|
|
|
// value is memoized to make sure the dependency is a singleton.
|
|
|
|
// If the dependency cannot be resolved, the resolution process continues and the dependency is represented as the none value.
|
|
|
|
IOOption() Dependency[IOO.IOOption[T]]
|
|
|
|
}
|
|
|
|
|
2023-12-19 12:18:07 +01:00
|
|
|
// MultiInjectionToken uniquely identifies a dependency by giving it an Id, Type and name that can have multiple implementations.
|
|
|
|
// Implementations are provided via the [MultiInjectionToken.Item] injection token.
|
2023-11-25 16:56:39 +01:00
|
|
|
type MultiInjectionToken[T any] interface {
|
|
|
|
// Container returns the injection token used to request an array of all provided items
|
|
|
|
Container() InjectionToken[[]T]
|
|
|
|
// Item returns the injection token used to provide an item
|
|
|
|
Item() InjectionToken[T]
|
|
|
|
}
|
|
|
|
|
|
|
|
// makeID creates a generator of unique string IDs
|
2024-02-07 10:07:37 +01:00
|
|
|
func makeID() IO.IO[string] {
|
2023-11-25 16:56:39 +01:00
|
|
|
var count atomic.Int64
|
|
|
|
return IO.MakeIO(func() string {
|
|
|
|
return strconv.FormatInt(count.Add(1), 16)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2024-02-07 10:07:37 +01:00
|
|
|
// genID is the common generator of unique string IDs
|
|
|
|
var genID = makeID()
|
2023-11-25 16:56:39 +01:00
|
|
|
|
2024-02-06 22:13:34 +01:00
|
|
|
type tokenBase struct {
|
2023-11-25 16:56:39 +01:00
|
|
|
name string
|
|
|
|
id string
|
|
|
|
flag int
|
|
|
|
providerFactory O.Option[DIE.ProviderFactory]
|
|
|
|
}
|
|
|
|
|
2024-02-06 22:13:34 +01:00
|
|
|
type token[T any] struct {
|
|
|
|
base *tokenBase
|
|
|
|
toType func(val any) E.Either[error, T]
|
|
|
|
}
|
|
|
|
|
2023-11-25 16:56:39 +01:00
|
|
|
func (t *token[T]) Id() string {
|
2024-02-06 22:13:34 +01:00
|
|
|
return t.base.id
|
2023-11-25 16:56:39 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
func (t *token[T]) Flag() int {
|
2024-02-06 22:13:34 +01:00
|
|
|
return t.base.flag
|
2023-11-25 16:56:39 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
func (t *token[T]) String() string {
|
2024-02-06 22:13:34 +01:00
|
|
|
return t.base.name
|
2023-11-25 16:56:39 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
func (t *token[T]) Unerase(val any) E.Either[error, T] {
|
|
|
|
return t.toType(val)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (t *token[T]) ProviderFactory() O.Option[DIE.ProviderFactory] {
|
2024-02-06 22:13:34 +01:00
|
|
|
return t.base.providerFactory
|
|
|
|
}
|
|
|
|
func makeTokenBase(name string, id string, typ int, providerFactory O.Option[DIE.ProviderFactory]) *tokenBase {
|
|
|
|
return &tokenBase{name, id, typ, providerFactory}
|
2023-11-25 16:56:39 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
func makeToken[T any](name string, id string, typ int, unerase func(val any) E.Either[error, T], providerFactory O.Option[DIE.ProviderFactory]) Dependency[T] {
|
2024-02-06 22:13:34 +01:00
|
|
|
return &token[T]{makeTokenBase(name, id, typ, providerFactory), unerase}
|
2023-11-25 16:56:39 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
type injectionToken[T any] struct {
|
|
|
|
token[T]
|
|
|
|
option Dependency[O.Option[T]]
|
|
|
|
ioeither Dependency[IOE.IOEither[error, T]]
|
|
|
|
iooption Dependency[IOO.IOOption[T]]
|
|
|
|
}
|
|
|
|
|
|
|
|
type multiInjectionToken[T any] struct {
|
|
|
|
container *injectionToken[[]T]
|
|
|
|
item *injectionToken[T]
|
|
|
|
}
|
|
|
|
|
|
|
|
func (i *injectionToken[T]) Identity() Dependency[T] {
|
|
|
|
return i
|
|
|
|
}
|
|
|
|
|
|
|
|
func (i *injectionToken[T]) Option() Dependency[O.Option[T]] {
|
|
|
|
return i.option
|
|
|
|
}
|
|
|
|
|
|
|
|
func (i *injectionToken[T]) IOEither() Dependency[IOE.IOEither[error, T]] {
|
|
|
|
return i.ioeither
|
|
|
|
}
|
|
|
|
|
|
|
|
func (i *injectionToken[T]) IOOption() Dependency[IOO.IOOption[T]] {
|
|
|
|
return i.iooption
|
|
|
|
}
|
|
|
|
|
|
|
|
func (i *injectionToken[T]) ProviderFactory() O.Option[DIE.ProviderFactory] {
|
2024-02-06 22:13:34 +01:00
|
|
|
return i.base.providerFactory
|
2023-11-25 16:56:39 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
func (m *multiInjectionToken[T]) Container() InjectionToken[[]T] {
|
|
|
|
return m.container
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *multiInjectionToken[T]) Item() InjectionToken[T] {
|
|
|
|
return m.item
|
|
|
|
}
|
|
|
|
|
2023-12-19 12:18:07 +01:00
|
|
|
// makeToken create a unique [InjectionToken] for a specific type
|
2023-11-25 16:56:39 +01:00
|
|
|
func makeInjectionToken[T any](name string, providerFactory O.Option[DIE.ProviderFactory]) InjectionToken[T] {
|
2024-02-07 10:07:37 +01:00
|
|
|
id := genID()
|
2023-11-25 16:56:39 +01:00
|
|
|
toIdentity := toType[T]()
|
|
|
|
return &injectionToken[T]{
|
2024-02-06 22:13:34 +01:00
|
|
|
token[T]{makeTokenBase(name, id, DIE.Identity, providerFactory), toIdentity},
|
2023-11-25 16:56:39 +01:00
|
|
|
makeToken[O.Option[T]](fmt.Sprintf("Option[%s]", name), id, DIE.Option, toOptionType(toIdentity), providerFactory),
|
|
|
|
makeToken[IOE.IOEither[error, T]](fmt.Sprintf("IOEither[%s]", name), id, DIE.IOEither, toIOEitherType(toIdentity), providerFactory),
|
|
|
|
makeToken[IOO.IOOption[T]](fmt.Sprintf("IOOption[%s]", name), id, DIE.IOOption, toIOOptionType(toIdentity), providerFactory),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-12-19 12:18:07 +01:00
|
|
|
// MakeToken create a unique [InjectionToken] for a specific type
|
2023-11-25 16:56:39 +01:00
|
|
|
func MakeToken[T any](name string) InjectionToken[T] {
|
|
|
|
return makeInjectionToken[T](name, O.None[DIE.ProviderFactory]())
|
|
|
|
}
|
|
|
|
|
2023-12-19 12:18:07 +01:00
|
|
|
// MakeToken create a unique [InjectionToken] for a specific type
|
2023-11-25 16:56:39 +01:00
|
|
|
func MakeTokenWithDefault[T any](name string, providerFactory DIE.ProviderFactory) InjectionToken[T] {
|
|
|
|
return makeInjectionToken[T](name, O.Of(providerFactory))
|
|
|
|
}
|
|
|
|
|
|
|
|
// MakeMultiToken creates a [MultiInjectionToken]
|
|
|
|
func MakeMultiToken[T any](name string) MultiInjectionToken[T] {
|
2024-02-07 10:07:37 +01:00
|
|
|
id := genID()
|
2023-11-25 16:56:39 +01:00
|
|
|
toItem := toType[T]()
|
|
|
|
toContainer := toArrayType(toItem)
|
|
|
|
containerName := fmt.Sprintf("Container[%s]", name)
|
|
|
|
itemName := fmt.Sprintf("Item[%s]", name)
|
|
|
|
// empty factory
|
|
|
|
providerFactory := O.None[DIE.ProviderFactory]()
|
|
|
|
// container
|
|
|
|
container := &injectionToken[[]T]{
|
2024-02-06 22:13:34 +01:00
|
|
|
token[[]T]{makeTokenBase(containerName, id, DIE.Multi|DIE.Identity, providerFactory), toContainer},
|
2023-11-25 16:56:39 +01:00
|
|
|
makeToken[O.Option[[]T]](fmt.Sprintf("Option[%s]", containerName), id, DIE.Multi|DIE.Option, toOptionType(toContainer), providerFactory),
|
|
|
|
makeToken[IOE.IOEither[error, []T]](fmt.Sprintf("IOEither[%s]", containerName), id, DIE.Multi|DIE.IOEither, toIOEitherType(toContainer), providerFactory),
|
|
|
|
makeToken[IOO.IOOption[[]T]](fmt.Sprintf("IOOption[%s]", containerName), id, DIE.Multi|DIE.IOOption, toIOOptionType(toContainer), providerFactory),
|
|
|
|
}
|
|
|
|
// item
|
|
|
|
item := &injectionToken[T]{
|
2024-02-06 22:13:34 +01:00
|
|
|
token[T]{makeTokenBase(itemName, id, DIE.Item|DIE.Identity, providerFactory), toItem},
|
2023-11-25 16:56:39 +01:00
|
|
|
makeToken[O.Option[T]](fmt.Sprintf("Option[%s]", itemName), id, DIE.Item|DIE.Option, toOptionType(toItem), providerFactory),
|
|
|
|
makeToken[IOE.IOEither[error, T]](fmt.Sprintf("IOEither[%s]", itemName), id, DIE.Item|DIE.IOEither, toIOEitherType(toItem), providerFactory),
|
|
|
|
makeToken[IOO.IOOption[T]](fmt.Sprintf("IOOption[%s]", itemName), id, DIE.Item|DIE.IOOption, toIOOptionType(toItem), providerFactory),
|
|
|
|
}
|
|
|
|
// returns the token
|
|
|
|
return &multiInjectionToken[T]{container, item}
|
|
|
|
}
|