package result_test import ( "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.Errf[string]("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 result.Errw[int](port.Err(), "result.Of") } if port.Value() < 1 || port.Value() > 65535 { return result.Errf[int]("%d out of range", port.Value()) } return port } // 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_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: example_test.go:22: result.Of // 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) // NOTE: you can have stack trace of an error as well // fmt.Println("stacktrace:", result.StackTrace(err)) } // Output: // caught: read host // example_test.go:13: host must not be empty } // 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 // example_test.go:25: 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") }).Must() fmt.Println(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 // example_test.go:25: 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_all shows result.All collecting values from concurrently running // goroutines. All goroutines are started before any result is read. func Example_all() { aCh := result.Async(func() int { return parsePort("80").Must() }) bCh := result.Async(func() int { return parsePort("443").Must() }) cCh := result.Async(func() int { return parsePort("8080").Must() }) ports := result.All(aCh, bCh, cCh).Must() fmt.Println(ports) // Output: // [80 443 8080] } // Example_allError shows result.All returning on the first error (in channel // order). Here bCh fails, so its error is returned and cCh is never read. func Example_allError() { aCh := result.Async(func() int { return parsePort("80").Must() }) bCh := result.Async(func() int { return parsePort("99999").Must() }) cCh := result.Async(func() int { return parsePort("8080").Must() }) if err := result.All(aCh, bCh, cCh).Err(); err != nil { fmt.Println("failed:", err) } // Output: // failed: example_test.go:25: 99999 out of range } // Example_map shows result.Map running a function concurrently over a slice. // All goroutines complete and all errors are collected. func Example_map() { ports, err := result.Map( []string{"80", "443", "8080"}, func(s string) int { return parsePort(s).Must() }, ).Unwrap() if err != nil { fmt.Println("failed:", err) return } fmt.Println(ports) // Output: // [80 443 8080] } // Example_mapError shows result.Map collecting all errors when multiple inputs // fail, rather than stopping at the first. func Example_mapError() { _, err := result.Map( []string{"80", "bad", "99999"}, func(s string) int { return parsePort(s).Must() }, ).Unwrap() if err != nil { fmt.Println("failed:", err) } // Output: // failed: example_test.go:22: result.Of // strconv.Atoi: parsing "bad": invalid syntax // example_test.go:25: 99999 out of range } // Example_asyncOf shows result.AsyncOf running library functions (which return // Expect[T]) concurrently. Only one .Expect() per goroutine is needed — at the // collection site inside the boundary. func Example_asyncOf() { hostCh := result.AsyncOf(result.Bind(parseHost, "localhost")) portCh := result.AsyncOf(func() result.Expect[int] { return parsePort("8080") }) url, err := result.Go(func() string { host := (<-hostCh).Expect("parse host") port := (<-portCh).Expect("parse port") return fmt.Sprintf("http://%s:%d", host, port) }).Unwrap() if err != nil { fmt.Println("failed:", err) return } fmt.Println(url) // Output: // http://localhost:8080 } // Example_asyncOfError shows result.AsyncOf propagating a failure through the // channel — the error surfaces at the .Expect() call inside the boundary. func Example_asyncOfError() { hostCh := result.AsyncOf(func() result.Expect[string] { return parseHost("") }) portCh := result.AsyncOf(func() result.Expect[int] { return parsePort("8080") }) _, err := result.Go(func() string { host := (<-hostCh).Expect("parse host") port := (<-portCh).Expect("parse port") return fmt.Sprintf("http://%s:%d", host, port) }).Unwrap() if err != nil { fmt.Println("failed:", err) } // Output: // failed: parse host // example_test.go:13: host must not be empty }