-
Notifications
You must be signed in to change notification settings - Fork 1
/
elm.go
103 lines (89 loc) · 2.34 KB
/
elm.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
package elmgo
import (
"context"
)
// Elmgo __ __
// .' '.' `.
// _.-| o | o |-._
// .~ `.__.'.__.'^ ~.
// .~ ^ / \ ^ ~.
// \-._^ ^| | ^_.-/
// `\ `-._ \___/ ^_.-' /'
// `\_ `--...--' /'
// `-.._______..-' /\ /\
// __/ \__ | |/ /_
// .'^ ^ `. .' `__\
// .' ^ ^ `.__.'^ .\ \
// .' ^ . ^ . ^ .' \/
// / / ^ \'.__.'
// | ^ /| ^ |
// \ \|^ ^ |
// `\^ | ^ |
// `~| ^ |
// | ^ ^ |
// \^ /
// `. ^ .'
// jgs : ^ ;
// .-~~~~~~ | ^ ~~~~~~-.
// / ^ ^ | ^ \
// \^ ^ / \ ^ ^ /
//go:generate go run github.com/maxbrunsfeld/counterfeiter/v6 -generate
//counterfeiter:generate . Dispatcher
type Dispatcher[Msg any] interface {
Dispatch(Msg)
}
type ChannelDispatcher[Msg any] struct {
dispatchCh chan<- Msg
}
func (d *ChannelDispatcher[Msg]) Dispatch(msg Msg) {
d.dispatchCh <- msg
}
//counterfeiter:generate . Renderer
type Renderer[A any] interface {
Render(A)
}
type Cmd interface {
Run() error
}
//counterfeiter:generate . Elmable
type Elmable[Model any, Msg any, Renderable any] interface {
Init() Model
Update(Msg, Model) (Model, Cmd)
View(Model, Dispatcher[Msg]) Renderable
}
type App[Model any, Msg any, Renderable any] struct {
elmable Elmable[Model, Msg, Renderable]
renderer Renderer[Renderable]
}
// Wow these generics are gross
func NewApp[Model any, Msg any, Renderable any](elmable Elmable[Model, Msg, Renderable], renderer Renderer[Renderable]) *App[Model, Msg, Renderable] {
return &App[Model, Msg, Renderable]{
elmable: elmable,
renderer: renderer,
}
}
func (a *App[Model, Msg, Renderable]) Run(ctx context.Context) chan struct{} {
doneCh := make(chan struct{})
dispatchCh := make(chan Msg)
// Get initial model and render the view
model := a.elmable.Init()
a.renderer.Render(a.elmable.View(model, &ChannelDispatcher[Msg]{
dispatchCh: dispatchCh,
}))
go func() {
for {
select {
case <-ctx.Done():
close(doneCh)
return
case msg := <-dispatchCh:
// Note: this is mutating the model in place
model, _ = a.elmable.Update(msg, model)
a.renderer.Render(a.elmable.View(model, &ChannelDispatcher[Msg]{
dispatchCh: dispatchCh,
}))
}
}
}()
return doneCh
}