159 lines
4.2 KiB
Go
159 lines
4.2 KiB
Go
package result_test
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"strconv"
|
|
|
|
"gitea.djmil.dev/go/template/pkg/result"
|
|
)
|
|
|
|
// parsePort wraps strconv.Atoi so callers can use the happy-path style.
|
|
func parsePort(s string) result.Expect[int] {
|
|
n, err := strconv.Atoi(s)
|
|
if err != nil {
|
|
return result.Fail[int](fmt.Errorf("parsePort: %w", err))
|
|
}
|
|
if n < 1 || n > 65535 {
|
|
return result.Fail[int](fmt.Errorf("parsePort: %d out of range", n))
|
|
}
|
|
return result.Ok(n)
|
|
}
|
|
|
|
// Example_happyPath shows the basic happy-path pattern: call Expect at each
|
|
// step; if anything fails the panic unwinds to the nearest Catch.
|
|
func Example_happyPath() {
|
|
port := parsePort("8080").Expect("read port")
|
|
fmt.Println(port)
|
|
// Output:
|
|
// 8080
|
|
}
|
|
|
|
// Example_errCheck shows checking the error without panicking — useful at the
|
|
// outermost boundary where you want a normal error return.
|
|
func Example_errCheck() {
|
|
r := parsePort("not-a-number")
|
|
if r.Err() != nil {
|
|
fmt.Println("failed:", r.Err())
|
|
}
|
|
// Output:
|
|
// failed: parsePort: strconv.Atoi: parsing "not-a-number": invalid syntax
|
|
}
|
|
|
|
// Example_of shows wrapping an existing (value, error) function with result.Of.
|
|
func Example_of() {
|
|
port := result.Of(strconv.Atoi("9090")).Expect("parse port")
|
|
fmt.Println(port)
|
|
// Output:
|
|
// 9090
|
|
}
|
|
|
|
// Example_go shows result.Go running a closure in a goroutine and returning
|
|
// a typed Expect[T] value — the happy-path result or a collected error.
|
|
func Example_go() {
|
|
port := result.Go(func() int {
|
|
return parsePort("8080").Expect("parse port")
|
|
})
|
|
|
|
fmt.Println(port.Expect("main"))
|
|
// Output:
|
|
// 8080
|
|
}
|
|
|
|
// Example_goError shows result.Go collecting an error from a failed Expect
|
|
// call inside the goroutine.
|
|
func Example_goError() {
|
|
port := result.Go(func() int {
|
|
return parsePort("99999").Expect("parse port")
|
|
})
|
|
|
|
if err := port.Err(); err != nil {
|
|
fmt.Println("failed:", err)
|
|
}
|
|
// Output:
|
|
// failed: parse port: parsePort: 99999 out of range
|
|
}
|
|
|
|
// Example_catch shows the boundary pattern: result.Run collects any
|
|
// Expect/Expectf failure anywhere in the call stack as a normal error.
|
|
func Example_catch() {
|
|
err := result.Run(func() {
|
|
// Simulate a deep call stack: this exits the goroutine because the port is invalid.
|
|
_ = parsePort("99999").Expect("load config port")
|
|
})
|
|
|
|
if err != nil {
|
|
fmt.Println("caught:", err)
|
|
}
|
|
// Output:
|
|
// caught: load config port: parsePort: 99999 out of range
|
|
}
|
|
|
|
// Example_unwrap shows re-joining the normal Go (value, error) world at a
|
|
// boundary where both values are needed separately.
|
|
func Example_unwrap() {
|
|
port, err := parsePort("443").Unwrap()
|
|
if err != nil {
|
|
fmt.Println("error:", err)
|
|
return
|
|
}
|
|
fmt.Println(port)
|
|
// Output:
|
|
// 443
|
|
}
|
|
|
|
// Example_mustCollected shows that Must() panics (produced by this package)
|
|
// are collected by Go/Run as normal errors, just like Expect failures.
|
|
// Genuine runtime panics (nil-deref, index out of bounds) are NOT collected —
|
|
// they propagate and crash the program, as they should.
|
|
func Example_mustCollected() {
|
|
err := result.Run(func() {
|
|
result.Fail[int](errors.New("unrecoverable")).Must()
|
|
})
|
|
|
|
if err != nil {
|
|
fmt.Println("collected:", err)
|
|
}
|
|
// Output:
|
|
// collected: unrecoverable
|
|
}
|
|
|
|
// Example_expectf shows Expectf for context messages that include runtime
|
|
// values — equivalent to Expect(fmt.Sprintf(...)) but more concise.
|
|
func Example_expectf() {
|
|
port := parsePort("3000").Expectf("read port from arg %d", 1)
|
|
fmt.Println(port)
|
|
// Output:
|
|
// 3000
|
|
}
|
|
|
|
// Example_expectfError shows that Expectf annotates the error message with the
|
|
// formatted context, just like Expect does.
|
|
func Example_expectfError() {
|
|
err := result.Run(func() {
|
|
_ = parsePort("99999").Expectf("arg %d port value", 2)
|
|
})
|
|
|
|
if err != nil {
|
|
fmt.Println("caught:", err)
|
|
}
|
|
// Output:
|
|
// caught: arg 2 port value: parsePort: 99999 out of range
|
|
}
|
|
|
|
// Example_fail shows constructing a failed Expect explicitly, e.g. when a
|
|
// function detects an error condition before calling any fallible op.
|
|
func Example_fail() {
|
|
validate := func(name string) result.Expect[string] {
|
|
if name == "" {
|
|
return result.Fail[string](errors.New("name must not be empty"))
|
|
}
|
|
return result.Ok(name)
|
|
}
|
|
|
|
r := validate("")
|
|
fmt.Println(r.Err())
|
|
// Output:
|
|
// name must not be empty
|
|
}
|