1
0
mirror of https://github.com/IBM/fp-go.git synced 2025-11-29 22:38:29 +02:00
Files
fp-go/di/token.go
Carsten Leue 16f708d69f fix: add initial DI implementation
Signed-off-by: Carsten Leue <carsten.leue@de.ibm.com>
2023-11-09 19:44:11 +01:00

129 lines
4.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]]
}
// 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
typ DIE.TokenType
toType func(val any) E.Either[error, T]
}
func (t *token[T]) Id() string {
return t.id
}
func (t *token[T]) Type() DIE.TokenType {
return t.typ
}
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 makeToken[T any](name string, id string, typ DIE.TokenType, unerase func(val any) E.Either[error, T]) Dependency[T] {
return &token[T]{name, id, typ, unerase}
}
type injectionToken[T any] struct {
token[T]
option Dependency[O.Option[T]]
ioeither Dependency[IOE.IOEither[error, T]]
iooption Dependency[IOO.IOOption[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
}
// MakeToken create a unique `InjectionToken` for a specific type
func MakeToken[T any](name string) InjectionToken[T] {
id := genId()
return &injectionToken[T]{
token[T]{name, id, DIE.Identity, toType[T]()},
makeToken[O.Option[T]](fmt.Sprintf("Option[%s]", name), id, DIE.Option, toOptionType[T]()),
makeToken[IOE.IOEither[error, T]](fmt.Sprintf("IOEither[%s]", name), id, DIE.IOEither, toIOEitherType[T]()),
makeToken[IOO.IOOption[T]](fmt.Sprintf("IOOption[%s]", name), id, DIE.IOOption, toIOOptionType[T]()),
}
}