Skip to content

Commit 033f525

Browse files
committed
Initial commit
0 parents  commit 033f525

11 files changed

+422
-0
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
*.log

.vscode/settings.json

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"makefile.extensionOutputFolder": "./.vscode"
3+
}

go.mod

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
module github.com/sjohnsonaz/go-headwater
2+
3+
go 1.18

injection.go

+64
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
package headwater
2+
3+
type Injector[T any] interface {
4+
Get() (bool, T)
5+
}
6+
7+
type valueInjector[T any] struct {
8+
value T
9+
}
10+
11+
func (i *valueInjector[T]) Get() (bool, T) {
12+
if i == nil {
13+
return false, GetZero[T]()
14+
}
15+
return true, i.value
16+
}
17+
18+
func CreateValue[T any](value T) Injector[T] {
19+
return &valueInjector[T]{value}
20+
}
21+
22+
type factory[T any] func() T
23+
24+
type factoryInjector[T any] struct {
25+
factory factory[T]
26+
}
27+
28+
func (i *factoryInjector[T]) Get() (bool, T) {
29+
if i == nil {
30+
return false, GetZero[T]()
31+
}
32+
return true, i.factory()
33+
}
34+
35+
func CreateFactory[T any](factory factory[T]) Injector[T] {
36+
return &factoryInjector[T]{factory}
37+
}
38+
39+
type singletonInjector[T any] struct {
40+
factory factory[T]
41+
value T
42+
ok bool
43+
done bool
44+
}
45+
46+
func (i *singletonInjector[T]) Get() (bool, T) {
47+
if i == nil {
48+
return false, GetZero[T]()
49+
}
50+
if !i.done {
51+
i.done = true
52+
i.ok = true
53+
i.value = i.factory()
54+
}
55+
return i.ok, i.value
56+
}
57+
58+
func CreateSingleton[T any](factory factory[T]) Injector[T] {
59+
return &singletonInjector[T]{
60+
factory: factory,
61+
ok: false,
62+
done: false,
63+
}
64+
}

injection_test.go

+123
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
package headwater
2+
3+
import "testing"
4+
5+
type DataAccess struct {
6+
Id int
7+
}
8+
9+
type ApiClient struct {
10+
Id int
11+
Url string
12+
}
13+
14+
type Container struct {
15+
Url Injector[string]
16+
DataAccess Injector[*DataAccess]
17+
ApiClient Injector[*ApiClient]
18+
}
19+
20+
func CreateContainer() Container {
21+
var id = 0
22+
getId := func() int {
23+
id += 1
24+
return id
25+
}
26+
var injection Container
27+
injection = Container{
28+
Url: CreateValue("http://localhost"),
29+
DataAccess: CreateFactory(func() *DataAccess {
30+
return &DataAccess{
31+
Id: getId(),
32+
}
33+
}),
34+
ApiClient: CreateSingleton(func() *ApiClient {
35+
ok, url := injection.Url.Get()
36+
if !ok {
37+
url = "http://127.0.0.1"
38+
}
39+
40+
return &ApiClient{
41+
Id: getId(),
42+
Url: url,
43+
}
44+
}),
45+
}
46+
return injection
47+
}
48+
49+
func TestValueInjector(t *testing.T) {
50+
container := CreateContainer()
51+
ok, url := container.Url.Get()
52+
53+
if !ok {
54+
t.Error("Received not ok")
55+
}
56+
57+
want := "http://localhost"
58+
59+
if url != want {
60+
t.Errorf("Received: %v, Expected %v", url, want)
61+
}
62+
}
63+
64+
func TestFactoryInjector(t *testing.T) {
65+
container := CreateContainer()
66+
ok, dataAccess := container.DataAccess.Get()
67+
68+
if !ok {
69+
t.Error("Received not ok")
70+
}
71+
72+
if dataAccess == nil {
73+
t.Errorf("Received: %v", dataAccess)
74+
}
75+
76+
ok, nextDataAccess := container.DataAccess.Get()
77+
78+
if !ok {
79+
t.Error("Received not ok")
80+
}
81+
82+
if dataAccess == nextDataAccess {
83+
t.Errorf("Received the same instance")
84+
}
85+
}
86+
87+
func TestNestedInjector(t *testing.T) {
88+
container := CreateContainer()
89+
ok, apiClient := container.ApiClient.Get()
90+
91+
if !ok {
92+
t.Error("Received not ok")
93+
}
94+
95+
if apiClient == nil {
96+
t.Errorf("Received: %v", apiClient)
97+
}
98+
99+
want := "http://localhost"
100+
101+
if apiClient.Url != want {
102+
t.Errorf("Received: %v, Expected %v", apiClient.Url, want)
103+
}
104+
}
105+
106+
func TestSingletonInjector(t *testing.T) {
107+
container := CreateContainer()
108+
ok, apiClient := container.ApiClient.Get()
109+
110+
if !ok {
111+
t.Error("Received not ok")
112+
}
113+
114+
ok, nextApiClient := container.ApiClient.Get()
115+
116+
if !ok {
117+
t.Error("Received not ok")
118+
}
119+
120+
if apiClient != nextApiClient {
121+
t.Error("Received multiple instances")
122+
}
123+
}

iterator.go

+47
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
package headwater
2+
3+
func ForEach[T any](array []T, callback func(item T)) {
4+
for _, item := range array {
5+
callback(item)
6+
}
7+
}
8+
9+
func Map[T any, U any](array []T, callback func(item T) U) []U {
10+
var output []U = make([]U, len(array))
11+
for index, item := range array {
12+
result := callback(item)
13+
output[index] = result
14+
}
15+
return output
16+
}
17+
18+
func Reduce[T any, U any](array []T, callback func(target U, item T) U, target U) U {
19+
var output U = target
20+
for _, item := range array {
21+
output = callback(output, item)
22+
}
23+
return output
24+
}
25+
26+
func Filter[T any](array []T, callback func(item T) bool) []T {
27+
var output []T = make([]T, 0)
28+
for _, item := range array {
29+
valid := callback(item)
30+
if valid {
31+
output = append(output, item)
32+
}
33+
}
34+
return output
35+
}
36+
37+
func Equal[T comparable](a, b []T) bool {
38+
if len(a) != len(b) {
39+
return false
40+
}
41+
for i, v := range a {
42+
if v != b[i] {
43+
return false
44+
}
45+
}
46+
return true
47+
}

iterator_test.go

+92
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
package headwater
2+
3+
import (
4+
"fmt"
5+
"testing"
6+
)
7+
8+
func TestForEach(t *testing.T) {
9+
items := []int64{0, 1, 2}
10+
result := 0
11+
12+
ForEach(items, func(item int64) {
13+
result++
14+
})
15+
16+
want := 3
17+
18+
if result != want {
19+
t.Errorf("Count is wrong. Expected: %#v, Received: %#v", want, result)
20+
}
21+
}
22+
23+
// Map should create an array with a new value for each value of the original array
24+
func TestMapEach(t *testing.T) {
25+
items := []int64{0, 1, 2}
26+
27+
result := Map(items, func(item int64) string {
28+
return "" + fmt.Sprint(item)
29+
})
30+
31+
want := []string{"0", "1", "2"}
32+
33+
if !Equal(want, result) {
34+
t.Errorf("Count is wrong. Expected: %#v, Received: %#v", want, result)
35+
}
36+
}
37+
38+
func TestNestedMap(t *testing.T) {
39+
items := []int64{0, 1, 2}
40+
41+
result := Map(Map(Map(
42+
items,
43+
func(item int64) float64 {
44+
return float64(item) / 2
45+
}),
46+
func(item float64) string {
47+
return "" + fmt.Sprint(item)
48+
}),
49+
func(item string) string {
50+
return "a" + item
51+
})
52+
53+
want := []string{"a0", "a0.5", "a1"}
54+
55+
if !Equal(want, result) {
56+
t.Errorf("Count is wrong. Expected: %#v, Received: %#v", want, result)
57+
}
58+
}
59+
60+
func TestReduce(t *testing.T) {
61+
items := []int{0, 1, 2}
62+
target := 0
63+
64+
result := Reduce(items, func(target int, item int) int {
65+
return target + item
66+
}, target)
67+
68+
want := 3
69+
70+
if result != want {
71+
t.Errorf("Count is wrong. Expected: %#v, Received: %#v", want, result)
72+
}
73+
}
74+
75+
func TestFilter(t *testing.T) {
76+
items := []int{0, 3, 1, 4, 2, 5}
77+
78+
result := Filter(items, func(item int) bool {
79+
if item >= 3 {
80+
return true
81+
} else {
82+
return false
83+
}
84+
})
85+
86+
out := fmt.Sprintf("%v, %v", items, result)
87+
fmt.Println(out)
88+
89+
if !Equal(result, []int{3, 4, 5}) {
90+
t.Error("Count is wrong")
91+
}
92+
}

makefile

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
.PHONY: test
2+
test:
3+
go test ./...

mediator.go

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package headwater
2+
3+
import (
4+
"context"
5+
"fmt"
6+
)
7+
8+
type requestHandler[R any, T any] func(context context.Context, request R) (T, error)
9+
10+
type Receiver[R any, T any] struct {
11+
handler requestHandler[R, T]
12+
}
13+
14+
func (eh *Receiver[R, T]) Send(context context.Context, request R) (T, error) {
15+
if eh.handler == nil {
16+
return GetZero[T](), fmt.Errorf("no handler for request: %T", request)
17+
}
18+
return eh.handler(context, request)
19+
}
20+
21+
func (eh *Receiver[E, T]) SetHandler(handler requestHandler[E, T]) {
22+
eh.handler = handler
23+
}

0 commit comments

Comments
 (0)