package result import ( "errors" "fmt" "runtime/debug" ) // Expect holds either a value of type T or an error. type Expect[T any] struct { value T err error } // Ok wraps a successful value in an Expect. func Ok[T any](v T) Expect[T] { return Expect[T]{value: v} } // Fail wraps an error in an Expect. func Fail[T any](err error) Expect[T] { return Expect[T]{err: err} } // Of is a convenience constructor that bridges standard Go (value, error) // return signatures: // // result.Of(os.Open("file.txt")).Expect("open config") func Of[T any](v T, err error) Expect[T] { return Expect[T]{value: v, err: err} } // Err returns the wrapped error, or nil on success. func (r Expect[T]) Err() error { return r.err } // Must returns the value or panics with the wrapped error and a stack trace. // Prefer [Expect.Expect] — it adds a message that makes the panic site easy to // locate in logs. func (r Expect[T]) Must() T { if r.err != nil { panic(&stackError{err: r.err, stack: debug.Stack()}) } return r.value } // Expect returns the value or panics with the error annotated by msg and a // stack trace captured at this call site. // // data := Parse(raw).Expect("parse user input") func (r Expect[T]) Expect(msg string) T { if r.err != nil { panic(&stackError{ err: fmt.Errorf("%s: %w", msg, r.err), stack: debug.Stack(), }) } return r.value } // Unwrap returns the value and error in the standard Go (value, error) form. // Useful at the boundary where you want to re-join normal error-return code. func (r Expect[T]) Unwrap() (T, error) { return r.value, r.err } // Catch recovers a panic produced by [Expect.Must] or [Expect.Expect] and // stores it in *errp. Call it via defer at the entry point of any function // that runs a happy-path call stack: // // func run() (err error) { // defer result.Catch(&err) // ... // } // // The stored error retains its stack trace; retrieve it with [StackTrace]. // Panics that are not errors (e.g. nil-pointer dereferences) are re-panicked // so genuine bugs are not silently swallowed. func Catch(errp *error) { v := recover() if v == nil { return } if err, ok := v.(error); ok { *errp = err return } panic(v) // not an error — let it propagate } // StackTrace returns the stack trace captured when [Expect.Expect] or // [Expect.Must] panicked. Returns an empty string if err was not produced by // this package. func StackTrace(err error) string { var s *stackError if errors.As(err, &s) { return string(s.stack) } return "" } // stackError wraps an error with a stack trace captured at the panic site. type stackError struct { err error stack []byte } func (s *stackError) Error() string { return s.err.Error() } func (s *stackError) Unwrap() error { return s.err }