template/pkg/result/example_test.go
2026-04-07 20:32:27 +00:00

118 lines
3.0 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_catch shows the boundary pattern: defer Catch at the entry point so
// any Expect/Must panic anywhere in the call stack is captured as a normal
// error return.
func Example_catch() {
run := func() (err error) {
defer result.Catch(&err)
// Simulate a deep call stack: this panics because the port is invalid.
_ = parsePort("99999").Expect("load config port")
return nil
}
if err := run(); 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_nonErrorPanic shows that Catch does NOT swallow genuine runtime
// panics — only error panics produced by Must/Expect are captured.
func Example_nonErrorPanic() {
safeRun := func() (err error) {
defer func() {
// Outer recover catches the re-panic from Catch.
if v := recover(); v != nil {
err = fmt.Errorf("non-error panic: %v", v)
}
}()
defer result.Catch(&err)
panic("unexpected runtime problem") // not an error — Catch re-panics
}
if err := safeRun(); err != nil {
fmt.Println(err)
}
// Output:
// non-error panic: unexpected runtime problem
}
// 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
}