/
valueset.go
135 lines (113 loc) · 2.71 KB
/
valueset.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
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
package mvp
import (
"context"
"fmt"
"sync"
)
type ExpandableType struct {
StoredValues []AnyVal
}
type ExpandableObject interface {
// ValueAt(idx int) any
ValueSet() ValueSet
}
type ValueSet []any
func newValueSet() []any {
definedValuesSafety.once.Do(finalizeDefinedValues)
values := make([]any, len(definedValues))
for i, val := range definedValues {
values[i] = val.initialValue()
}
return values
}
var (
definedValues []AnyVal
definedValuesSafety struct {
final bool
mut sync.Mutex
once sync.Once
}
GoCtx = Value[context.Context]("goctx")
)
type undefined struct{}
func (_ undefined) String() string {
return "<undefined>"
}
func IsUndefined(v any) bool {
return v == undefined{}
}
type AnyVal interface {
Name() string
initialValue() any
}
type Val[T any] struct {
index int
name string
initializer func() T
}
func (val *Val[T]) Name() string {
return val.name
}
func (val *Val[T]) initialValue() any {
if val.initializer != nil {
return val.initializer()
}
return undefined{}
}
func (val *Val[T]) Get(o ExpandableObject) T {
switch v := o.ValueSet()[val.index].(type) {
case T:
return v
case undefined:
panic(fmt.Errorf("%s has no defined value in this context", val.name))
default:
panic("unreachable: unexpected type")
}
}
func (val *Val[T]) Set(o ExpandableObject, v T) {
o.ValueSet()[val.index] = v
}
func Value[T any](name string, opts ...func(val *Val[T])) *Val[T] {
definedValuesSafety.mut.Lock()
defer definedValuesSafety.mut.Unlock()
if definedValuesSafety.final {
panic("cannot define new values after Context has been created")
}
val := &Val[T]{
index: len(definedValues),
name: name,
}
for _, opt := range opts {
opt(val)
}
definedValues = append(definedValues, val)
return val
}
// ValueDefault is an option to pass to Value.
func ValueDefault[T any](v T) func(val *Val[T]) {
return ValueInitializer(func() T { return v })
}
// ValueZeroInit is an option to pass to Value.
func ValueZeroInit[T any](val *Val[T]) {
val.initializer = func() T {
var zero T
return zero
}
}
// ValueInitializer is an option to pass to Value.
func ValueInitializer[T any](f func() T) func(val *Val[T]) {
return func(val *Val[T]) { val.initializer = f }
}
// ValueDep is an option to pass to Value. It signals that this value should be
// initialized after those other values.
func ValueDep[T any](deps ...AnyVal) func(val *Val[T]) {
return func(val *Val[T]) {
// No code necessary: just because we're referencing other values,
// they will defined before the current value and thus will have lower indices.
}
}
func finalizeDefinedValues() {
definedValuesSafety.mut.Lock()
defer definedValuesSafety.mut.Unlock()
definedValuesSafety.final = true
}