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_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() { run := func() (err error) { defer result.Catch(&err) _ = parsePort("99999").Expectf("arg %d port value", 2) return nil } if err := run(); 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 }