aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/internal/cm/result.go
blob: 781dccc1a1614e10ddf4cd31526c41e03e3c5f00 (plain)
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
package cm

import "unsafe"

const (
	// ResultOK represents the OK case of a result.
	ResultOK = false

	// ResultErr represents the error case of a result.
	ResultErr = true
)

// BoolResult represents a result with no OK or error type.
// False represents the OK case and true represents the error case.
type BoolResult bool

// Result represents a result sized to hold the Shape type.
// The size of the Shape type must be greater than or equal to the size of OK and Err types.
// For results with two zero-length types, use [BoolResult].
type Result[Shape, OK, Err any] struct {
	_ HostLayout
	result[Shape, OK, Err]
}

// AnyResult is a type constraint for generic functions that accept any [Result] type.
type AnyResult[Shape, OK, Err any] interface {
	~struct {
		_ HostLayout
		result[Shape, OK, Err]
	}
}

// result represents the internal representation of a Component Model result type.
type result[Shape, OK, Err any] struct {
	_     HostLayout
	isErr bool
	_     [0]OK
	_     [0]Err
	data  Shape // [unsafe.Sizeof(*(*Shape)(unsafe.Pointer(nil)))]byte
}

// IsOK returns true if r represents the OK case.
func (r *result[Shape, OK, Err]) IsOK() bool {
	r.validate()
	return !r.isErr
}

// IsErr returns true if r represents the error case.
func (r *result[Shape, OK, Err]) IsErr() bool {
	r.validate()
	return r.isErr
}

// OK returns a non-nil *OK pointer if r represents the OK case.
// If r represents an error, then it returns nil.
func (r *result[Shape, OK, Err]) OK() *OK {
	r.validate()
	if r.isErr {
		return nil
	}
	return (*OK)(unsafe.Pointer(&r.data))
}

// Err returns a non-nil *Err pointer if r represents the error case.
// If r represents the OK case, then it returns nil.
func (r *result[Shape, OK, Err]) Err() *Err {
	r.validate()
	if !r.isErr {
		return nil
	}
	return (*Err)(unsafe.Pointer(&r.data))
}

// Result returns (OK, zero value of Err, false) if r represents the OK case,
// or (zero value of OK, Err, true) if r represents the error case.
// This does not have a pointer receiver, so it can be chained.
func (r result[Shape, OK, Err]) Result() (ok OK, err Err, isErr bool) {
	if r.isErr {
		return ok, *(*Err)(unsafe.Pointer(&r.data)), true
	}
	return *(*OK)(unsafe.Pointer(&r.data)), err, false
}

// This function is sized so it can be inlined and optimized away.
func (r *result[Shape, OK, Err]) validate() {
	var shape Shape
	var ok OK
	var err Err

	// Check if size of Shape is greater than both OK and Err
	if unsafe.Sizeof(shape) > unsafe.Sizeof(ok) && unsafe.Sizeof(shape) > unsafe.Sizeof(err) {
		panic("result: size of data type > OK and Err types")
	}

	// Check if size of OK is greater than Shape
	if unsafe.Sizeof(ok) > unsafe.Sizeof(shape) {
		panic("result: size of OK type > data type")
	}

	// Check if size of Err is greater than Shape
	if unsafe.Sizeof(err) > unsafe.Sizeof(shape) {
		panic("result: size of Err type > data type")
	}

	// Check if Shape is zero-sized, but size of result != 1
	if unsafe.Sizeof(shape) == 0 && unsafe.Sizeof(*r) != 1 {
		panic("result: size of data type == 0, but result size != 1")
	}
}

// OK returns an OK result with shape Shape and type OK and Err.
// Pass Result[OK, OK, Err] or Result[Err, OK, Err] as the first type argument.
func OK[R AnyResult[Shape, OK, Err], Shape, OK, Err any](ok OK) R {
	var r Result[Shape, OK, Err]
	r.validate()
	r.isErr = ResultOK
	*((*OK)(unsafe.Pointer(&r.data))) = ok
	return R(r)
}

// Err returns an error result with shape Shape and type OK and Err.
// Pass Result[OK, OK, Err] or Result[Err, OK, Err] as the first type argument.
func Err[R AnyResult[Shape, OK, Err], Shape, OK, Err any](err Err) R {
	var r Result[Shape, OK, Err]
	r.validate()
	r.isErr = ResultErr
	*((*Err)(unsafe.Pointer(&r.data))) = err
	return R(r)
}