template/pkg/result/example_test.go
2026-05-04 22:45:33 +00:00

146 lines
3.8 KiB
Go

package result_test
import (
"errors"
"fmt"
"strconv"
"gitea.djmil.dev/go/template/pkg/result"
)
// parseHost is an example of a simple utility function, that validates a hostname.
func parseHost(s string) result.Expect[string] {
if s == "" {
return result.Fail[string](errors.New("host must not be empty"))
}
return result.Ok(s)
}
// parsePort wraps strconv.Atoi so callers can use the happy-path style.
func parsePort(s string) result.Expect[int] {
port := result.Of(strconv.Atoi(s))
if port.Err() != nil {
return port
}
if port.Value() < 1 || port.Value() > 65535 {
return result.Fail[int](fmt.Errorf("%d out of range", port.Value()))
}
return port
}
// 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("parsePort failed:", r.Err())
}
// Output:
// parsePort failed: strconv.Atoi: parsing "not-a-number": invalid syntax
}
// 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() {
host := parseHost("localhost").Expect("read host")
fmt.Println(host)
// Output:
// localhost
}
// Example_catch shows the failure path: when Expect exits the goroutine,
// defer Catch at the entry point converts it into a normal error return.
func Example_catch() {
loadHost := func() (err error) {
defer result.Catch(&err)
host := parseHost("").Expect("read host")
fmt.Println(host)
return
}
if err := loadHost(); err != nil {
fmt.Println("caught:", err)
}
// Output:
// caught: read host: host must not be empty
}
// getURL is an example of business logic implementation with emphasis on happy-path.
func getURL(host, port string) string {
mHost := parseHost(host).Expect("parse host")
mPort := parsePort(port).Expect("parse port")
return fmt.Sprintf("http://%s:%d", mHost, mPort)
}
// Example_run shows result.Run as a lightweight synchronous boundary — a concise
// alternative to defer Catch when no named return is needed. Expect calls may be
// chained freely inside.
func Example_run() {
err := result.Run(func() {
url := getURL("localhost", "8080")
fmt.Println(url)
})
if err != nil {
fmt.Println("failed:", err)
}
// Output:
// http://localhost:8080
}
// Example_runtimeError shows that Expectf annotates the error message with the
// formatted context, just like Expect does.
//
// NOTE: Genuine runtime panics (nil-deref, index out of bounds) are NOT collected,
// they propagate and crash the program, as they should.
func Example_runtimeError() {
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: 99999 out of range
}
// Example_go is like result.Run but returns a typed Expect[T] so the computed
// value can be retrieved. Any failure is collected and surfaced on that Expect.
func Example_go() {
url := result.Go(func() string {
return getURL("localhost", "8080")
})
fmt.Println(url.Expect("get url"))
// Output:
// http://localhost:8080
}
// Example_goError shows result.Go collecting an error when one of the chained
// Expect calls inside the goroutine fails.
func Example_goError() {
url := result.Go(func() string {
return getURL("localhost", "99999")
})
if err := url.Err(); err != nil {
fmt.Println("failed:", err)
}
// Output:
// failed: parse port: 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
}