diff --git a/CHANGELOG.md b/CHANGELOG.md index 84754322..bbed7c10 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), ### Added - `wit-bindgen-go` now accepts arguments that control the level of logging output on stderr. Verbose mode (`-v` or `--verbose`) will print informational log lines, warnings, and errors. Debug mode (`-vv` or `--debug`) will emit finer-grained debugging information. By default, `wit-bindgen-go` will print warnings or errors. +- New method `(cm.Result).Result() (ok OK, err Err, isErr bool)` added to streamline common patterns using `result` types. It has a value receiver, allowing it to be chained off a function call that returns a `Result`, eliminating the need to declare a temporary variable. For example: `stream, err, isErr := stream.BlockingRead(n)` ### Fixed diff --git a/cm/result.go b/cm/result.go index 82200e27..781dccc1 100644 --- a/cm/result.go +++ b/cm/result.go @@ -71,6 +71,16 @@ func (r *result[Shape, OK, Err]) Err() *Err { 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 diff --git a/cm/result_test.go b/cm/result_test.go index 56bf5b4e..dc8f7330 100644 --- a/cm/result_test.go +++ b/cm/result_test.go @@ -17,6 +17,48 @@ type resulter[OK, Err any] interface { IsErr() bool OK() *OK Err() *Err + Result() (OK, Err, bool) +} + +func TestResultOKOrErr(t *testing.T) { + r1 := OK[Result[string, string, struct{}]]("hello") + if ok := r1.OK(); ok == nil { + t.Errorf("OK(): %v, expected non-nil OK", ok) + } + if err := r1.Err(); err != nil { + t.Errorf("Err(): %v, expected nil Err", err) + } + + r2 := Err[Result[bool, struct{}, bool]](true) + if ok := r2.OK(); ok != nil { + t.Errorf("OK(): %v, expected nil OK", ok) + } + if err := r2.Err(); err == nil { + t.Errorf("Err(): %v, expected non-nil Err", err) + } +} + +func TestResultResult(t *testing.T) { + ok, err, isErr := OK[Result[string, string, int]]("hello").Result() + if got, want := ok, "hello"; got != want { + t.Errorf("Result(): ok = %v; expected %v", got, want) + } + if got, want := err, 0; got != want { + t.Errorf("Result(): err = %v; expected %v", got, want) + } + if got, want := isErr, false; got != want { + t.Errorf("Result(): isErr = %v; expected %v", got, want) + } + ok, err, isErr = Err[Result[string, string, int]](42).Result() + if got, want := ok, ""; got != want { + t.Errorf("Result(): ok = %v; expected %v", got, want) + } + if got, want := err, 42; got != want { + t.Errorf("Result(): err = %v; expected %v", got, want) + } + if got, want := isErr, true; got != want { + t.Errorf("Result(): isErr = %v; expected %v", got, want) + } } func TestResultLayout(t *testing.T) { @@ -70,24 +112,6 @@ func TestResultLayout(t *testing.T) { } } -func TestResultOKOrErr(t *testing.T) { - r1 := OK[Result[string, string, struct{}]]("hello") - if ok := r1.OK(); ok == nil { - t.Errorf("OK(): %v, expected non-nil OK", ok) - } - if err := r1.Err(); err != nil { - t.Errorf("Err(): %v, expected nil Err", err) - } - - r2 := Err[Result[bool, struct{}, bool]](true) - if ok := r2.OK(); ok != nil { - t.Errorf("OK(): %v, expected nil OK", ok) - } - if err := r2.Err(); err == nil { - t.Errorf("Err(): %v, expected non-nil Err", err) - } -} - func TestAltResult1(t *testing.T) { type alt1[Shape, OK, Err any] struct { _ [0]OK