mirror of
https://github.com/IBM/fp-go.git
synced 2025-08-10 22:31:32 +02:00
197 lines
7.2 KiB
Go
197 lines
7.2 KiB
Go
![]() |
// 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]
|
||
|
// 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]`
|
||
|
Option() Dependency[O.Option[T]]
|
||
|
// IOEither identifies this dependency as mandatory but it will be resolved lazily as a `IOE.IOEither[error, T]`. This
|
||
|
// 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]]
|
||
|
// IOOption identifies this dependency as optional but it will be resolved lazily as a `IOO.IOOption[T]`. This
|
||
|
// 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]]
|
||
|
}
|
||
|
|
||
|
// MultiInjectionToken uniquely identifies a dependency by giving it an Id, Type and name.
|
||
|
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
|
||
|
func makeId() IO.IO[string] {
|
||
|
var count atomic.Int64
|
||
|
return IO.MakeIO(func() string {
|
||
|
return strconv.FormatInt(count.Add(1), 16)
|
||
|
})
|
||
|
}
|
||
|
|
||
|
// genId is the common generator of unique string IDs
|
||
|
var genId = makeId()
|
||
|
|
||
|
type token[T any] struct {
|
||
|
name string
|
||
|
id string
|
||
|
flag int
|
||
|
toType func(val any) E.Either[error, T]
|
||
|
providerFactory O.Option[DIE.ProviderFactory]
|
||
|
}
|
||
|
|
||
|
func (t *token[T]) Id() string {
|
||
|
return t.id
|
||
|
}
|
||
|
|
||
|
func (t *token[T]) Flag() int {
|
||
|
return t.flag
|
||
|
}
|
||
|
|
||
|
func (t *token[T]) String() string {
|
||
|
return t.name
|
||
|
}
|
||
|
|
||
|
func (t *token[T]) Unerase(val any) E.Either[error, T] {
|
||
|
return t.toType(val)
|
||
|
}
|
||
|
|
||
|
func (t *token[T]) ProviderFactory() O.Option[DIE.ProviderFactory] {
|
||
|
return t.providerFactory
|
||
|
}
|
||
|
|
||
|
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] {
|
||
|
return &token[T]{name, id, typ, unerase, providerFactory}
|
||
|
}
|
||
|
|
||
|
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] {
|
||
|
return i.providerFactory
|
||
|
}
|
||
|
|
||
|
func (m *multiInjectionToken[T]) Container() InjectionToken[[]T] {
|
||
|
return m.container
|
||
|
}
|
||
|
|
||
|
func (m *multiInjectionToken[T]) Item() InjectionToken[T] {
|
||
|
return m.item
|
||
|
}
|
||
|
|
||
|
// makeToken create a unique `InjectionToken` for a specific type
|
||
|
func makeInjectionToken[T any](name string, providerFactory O.Option[DIE.ProviderFactory]) InjectionToken[T] {
|
||
|
id := genId()
|
||
|
toIdentity := toType[T]()
|
||
|
return &injectionToken[T]{
|
||
|
token[T]{name, id, DIE.Identity, toIdentity, providerFactory},
|
||
|
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),
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// MakeToken create a unique `InjectionToken` for a specific type
|
||
|
func MakeToken[T any](name string) InjectionToken[T] {
|
||
|
return makeInjectionToken[T](name, O.None[DIE.ProviderFactory]())
|
||
|
}
|
||
|
|
||
|
// MakeToken create a unique `InjectionToken` for a specific type
|
||
|
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] {
|
||
|
id := genId()
|
||
|
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]{
|
||
|
token[[]T]{containerName, id, DIE.Multi | DIE.Identity, toContainer, providerFactory},
|
||
|
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]{
|
||
|
token[T]{itemName, id, DIE.Item | DIE.Identity, toItem, providerFactory},
|
||
|
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}
|
||
|
}
|